Fossil SCM

More work/cleanup on the single-file commit.

stephan 2020-04-28 08:48 checkin-without-checkout
Commit 138c07be85232f05e93e23a4613380870bfd86f088cf6750dc86ddfce8ea1d3f
1 file changed +134 -52
+134 -52
--- src/checkin.c
+++ src/checkin.c
@@ -2678,11 +2678,11 @@
26782678
#if INTERFACE
26792679
/*
26802680
** State for the "web-checkin" infrastructure, which enables the
26812681
** ability to commit changes to a single file via an HTTP request.
26822682
*/
2683
-struct CheckinWebInfo {
2683
+struct CheckinOneFileInfo {
26842684
Blob comment; /* Check-in comment text */
26852685
char *zMimetype; /* Mimetype of check-in command. May be NULL */
26862686
Manifest * pParent; /* parent checkin */
26872687
const char *zUser; /* User name */
26882688
char *zFilename; /* Name of single file to commit. */
@@ -2689,53 +2689,65 @@
26892689
Blob fileContent; /* Content of the modified file. */
26902690
Blob fileHash; /* Hash of this->fileContent */
26912691
};
26922692
#endif /* INTERFACE */
26932693
2694
-static void CheckinWebInfo_init( CheckinWebInfo * p ){
2695
- memset(p, 0, sizeof(struct CheckinWebInfo));
2694
+static void CheckinOneFileInfo_init( CheckinOneFileInfo * p ){
2695
+ memset(p, 0, sizeof(struct CheckinOneFileInfo));
26962696
p->comment = p->fileContent = p->fileHash = empty_blob;
26972697
}
26982698
2699
-static void CheckinWebInfo_cleanup( CheckinWebInfo * p ){
2699
+static void CheckinOneFileInfo_cleanup( CheckinOneFileInfo * p ){
27002700
blob_reset(&p->comment);
27012701
blob_reset(&p->fileContent);
27022702
blob_reset(&p->fileHash);
27032703
if(p->pParent){
27042704
manifest_destroy(p->pParent);
27052705
}
27062706
fossil_free(p->zFilename);
27072707
fossil_free(p->zMimetype);
2708
- CheckinWebInfo_init(p);
2708
+ CheckinOneFileInfo_init(p);
27092709
}
27102710
27112711
/*
27122712
** Creates a manifest file, written to pOut, from the state in the
2713
-** fully-populated pCI argument. On error, returns non-0 and, if
2714
-** pErr is not NULL, writes an error message there.
2713
+** fully-populated pCI argument. Returns true on success. On error,
2714
+** returns 0 and, if pErr is not NULL, writes an error message there.
27152715
*/
2716
-static int create_manifest_web( Blob * pOut, CheckinWebInfo * pCI,
2717
- Blob * pErr){
2718
- Blob zCard = empty_blob;
2719
- ManifestFile *zFile;
2720
- int cmp = -99;
2721
- const char *zPerm = "";
2722
- const char *zFilename = 0;
2723
- const char *zUuid = 0;
2724
- int (*fncmp)(char const *, char const *) =
2716
+static int create_manifest_one_file( Blob * pOut, CheckinOneFileInfo * pCI,
2717
+ Blob * pErr){
2718
+ Blob zCard = empty_blob; /* Z-card checksum */
2719
+ ManifestFile *zFile; /* One file entry from the pCI->pParent */
2720
+ int cmp = -99; /* filename comparison result */
2721
+ int fperm = 0; /* file permissions */
2722
+ const char *zPerm = 0; /* permissions for new F-card */
2723
+ const char *zFilename = 0; /* filename to use for F-card */
2724
+ const char *zUuid = 0; /* UUID for F-card */
2725
+ const char *zErrMsg = 0; /* For error reporting. */
2726
+ int (*fncmp)(char const *, char const *) = /* filename comparator */
27252727
filenames_are_case_sensitive()
27262728
? fossil_strcmp
27272729
: fossil_stricmp;
27282730
2729
-#define mf_err(MSG) if(pErr) blob_append(pErr,MSG,-1); return 1
2731
+ assert(blob_str(&pCI->fileHash));
2732
+ assert(pCI->pParent);
2733
+ assert(pCI->zFilename);
2734
+ assert(pCI->zUser);
2735
+
2736
+#define mf_err(MSG) zErrMsg = MSG; goto manifest_error
27302737
/* Potential TODOs include...
27312738
** - Create a delta manifest, if possible, rather than a baseline.
27322739
** - Enable adding of new files? It's implemented by disabled until
27332740
** we at least ensure that pCI->zFilename is a path-relative
27342741
** filename.
27352742
** - Maybe add support for tags. Those can be edited via /info page.
27362743
** - Check for a commit to a closed leaf and re-open it.
2744
+ ** - Symlinks: if we're really in a checkout, handle commit/add of
2745
+ ** symlinks like a normal commit would. For now we bail out if
2746
+ ** told to handle a symlink because there would seem to be no(?)
2747
+ ** sensible way to handle a symlink add/checkin without a
2748
+ ** checkout.
27372749
*/
27382750
blob_zero(pOut);
27392751
if(blob_size(&pCI->comment)!=0){
27402752
blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
27412753
}else{
@@ -2745,54 +2757,58 @@
27452757
27462758
manifest_file_rewind(pCI->pParent);
27472759
while((zFile = manifest_file_next(pCI->pParent, 0))){
27482760
cmp = fncmp(zFile->zName, pCI->zFilename);
27492761
if(cmp<0){
2750
- blob_appendf(pOut, "F %F %s%s\n", zFile->zName, zFile->zUuid,
2751
- zFile->zPerm);
2762
+ blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2763
+ (zFile->zPerm && *zFile->zPerm) ? " " : "",
2764
+ (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
27522765
}else{
27532766
break;
27542767
}
27552768
}
27562769
if(cmp==0){ /* Match */
2757
- const int perm = manifest_file_mperm(zFile);
2758
- if(PERM_LNK==perm){
2759
- mf_err("Cannot commit symlinks with this approach.");
2760
- }
2761
- zPerm = PERM_EXE==perm ? " x" : "";
2770
+ fperm = manifest_file_mperm(zFile);
27622771
zFilename = zFile->zName
2763
- /* use original name in case of case difference */;
2772
+ /* use original name in case of name-case difference */;
27642773
zUuid = blob_str(&pCI->fileHash);
27652774
}else{
27662775
/* This is a new file. */
2776
+ fperm = file_perm(pCI->zFilename, ExtFILE);
27672777
zFilename = pCI->zFilename;
2768
- zPerm = "";
27692778
zUuid = blob_str(&pCI->fileHash);
27702779
if(cmp>0){
27712780
assert(zFile!=0);
27722781
assert(pCI->pParent->iFile>0);
27732782
--pCI->pParent->iFile
27742783
/* so that the next step picks up that entry again */;
27752784
}
2785
+ }
2786
+ if(PERM_LNK==fperm){
2787
+ mf_err("Cannot commit symlinks with this approach.");
2788
+ }else if(PERM_EXE==fperm){
2789
+ zPerm = " x";
2790
+ }else{
2791
+ zPerm = "";
27762792
}
27772793
assert(zFilename);
27782794
assert(zUuid);
27792795
assert(zPerm);
27802796
blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
27812797
while((zFile = manifest_file_next(pCI->pParent, 0))){
27822798
cmp = fncmp(zFile->zName, pCI->zFilename);
27832799
assert(cmp>0);
27842800
if(cmp<=0){
2785
- mf_err("Mis-ordering of F-cards.");
2801
+ mf_err("Internal error: mis-ordering of F-cards detected.");
27862802
}
27872803
blob_appendf(pOut, "F %F %s%s%s\n",
27882804
zFile->zName, zFile->zUuid,
27892805
(zFile->zPerm && *zFile->zPerm) ? " " : "",
27902806
(zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
27912807
}
27922808
2793
- if(pCI->zMimetype){
2809
+ if(pCI->zMimetype!=0 && pCI->zMimetype[0]!=0){
27942810
blob_appendf(pOut, "N %F\n", pCI->zMimetype);
27952811
}
27962812
27972813
blob_appendf(pOut, "P %z\n",
27982814
db_text(0,"SELECT uuid FROM blob WHERE rid=%d",
@@ -2800,120 +2816,186 @@
28002816
blob_appendf(pOut, "U %F\n", pCI->zUser);
28012817
md5sum_blob(pOut, &zCard);
28022818
blob_appendf(pOut, "Z %b\n", &zCard);
28032819
blob_reset(&zCard);
28042820
return 0;
2821
+manifest_error:
2822
+ assert( zErrMsg );
2823
+ if(pErr) blob_append(pErr,zErrMsg,-1);
2824
+ return 1;
28052825
#undef mf_err
28062826
}
28072827
28082828
/*
2809
-** EXPERIMENTAL! A so-called "web checkin" is a slimmed-down form of
2810
-** the checkin command which accepts only a single file and is
2811
-** intended to accept edits to a file via the web interface.
2829
+** EXPERIMENTAL! Subject to change or removal at any time.
2830
+**
2831
+** A so-called "single-file checkin" is a slimmed-down form of the
2832
+** checkin command which accepts only a single file and is intended to
2833
+** accept edits to a file via the web interface or from the CLI from
2834
+** outside of a checkout.
28122835
**
28132836
** This routine uses the state from the given fully-populated pCI
28142837
** argument to add pCI->fileContent to the database, and create and
2815
-** save a manifest for that change.
2838
+** save a manifest for that change. Ownership of pCI and its contents
2839
+** are unchanged.
28162840
**
28172841
** Fails fatally on error. If pRid is not NULL, the RID of the
28182842
** resulting manifest is written to *pRid. If bDryRun is true,
28192843
** it rolls back its transaction, else it commits as usual.
2820
-**
28212844
*/
2822
-void checkin_web( CheckinWebInfo * pCI, int *pRid, int bDryRun ){
2845
+void checkin_one_file( CheckinOneFileInfo * pCI, int *pRid, int bDryRun ){
28232846
Blob mf = empty_blob;
28242847
Blob err = empty_blob;
28252848
int rid = 0, frid = 0;
28262849
const int isPrivate = content_is_private(pCI->pParent->rid);
28272850
ManifestFile * zFile;
2851
+ const char * zFilePrevUuid = 0; /* UUID of previous version of
2852
+ the file */
28282853
28292854
db_begin_transaction();
28302855
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
28312856
fossil_fatal("no such user: %s", pCI->zUser);
28322857
}
2858
+ /*
2859
+ ** Confirm that pCI->zFilename can be found in pCI->pParent.
2860
+ ** If not, fail. This is admittedly an artificial limitation,
2861
+ ** not strictly necessary.
2862
+ */
28332863
manifest_file_rewind(pCI->pParent);
28342864
zFile = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
28352865
if(!zFile){
28362866
fossil_fatal("File [%s] not found in manifest. "
28372867
"Adding new files is currently not allowed.",
28382868
pCI->zFilename);
2869
+ }else if(zFile->zPerm && strstr(zFile->zPerm, "l")){
2870
+ fossil_fatal("Cannot save a symlink this way.");
2871
+ }else{
2872
+ if(0==fossil_strcmp(zFile->zUuid, blob_str(&pCI->fileHash))){
2873
+ fossil_fatal("File is unchanged. Not saving.");
2874
+ }
2875
+ zFilePrevUuid = zFile->zUuid;
28392876
}
2840
- if(create_manifest_web(&mf, pCI, &err)!=0){
2841
- fossil_fatal("create_manifest_web() failed: %B", &err);
2877
+ /* Create the manifest... */
2878
+ if(create_manifest_one_file(&mf, pCI, &err)==0){
2879
+ fossil_fatal("create_manifest_one_file() failed: %B", &err);
28422880
}
2881
+ /* Add the file content to the db... */
28432882
frid = content_put_ex(&pCI->fileContent, blob_str(&pCI->fileHash),
28442883
0, 0, isPrivate);
2845
- manifest_file_rewind(pCI->pParent);
2846
- zFile = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
2847
- if(zFile!=0){
2884
+ if(zFilePrevUuid!=0){
2885
+ /* Deltify against previous file version... */
28482886
int prevFRid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q",
2849
- zFile->zUuid);
2887
+ zFilePrevUuid);
28502888
assert(prevFRid>0);
28512889
content_deltify(frid, &prevFRid, 1, 0);
28522890
}
2891
+ /* Save and crosslink the manifest... */
28532892
rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
28542893
if(pRid!=0){
28552894
*pRid = rid;
28562895
}
2896
+#if 1
2897
+ /* Only for development/debugging... */
28572898
fossil_print("Manifest:\n%b", &mf);
2899
+#endif
28582900
manifest_crosslink(rid, &mf, 0);
28592901
if(bDryRun){
28602902
fossil_print("Rolling back transaction.\n");
28612903
}
28622904
db_end_transaction(bDryRun ? 1 : 0);
28632905
blob_reset(&mf);
28642906
}
28652907
28662908
/*
2867
-** COMMAND: test-ci-web
2909
+** COMMAND: test-ci-one
28682910
**
28692911
** This is an on-going experiment, subject to change or removal at
28702912
** any time.
28712913
**
28722914
** Usage: %fossil ?OPTIONS? FILENAME
28732915
**
28742916
** where FILENAME is a repo-relative name as it would appear in the
28752917
** vfile table.
2918
+**
2919
+** Options:
2920
+**
2921
+** --as FILENAME The repository-side name of the input file,
2922
+** relative to the top of the repostory.
2923
+** Default is the same as the input file name.
2924
+** --comment|-m COMMENT Optional checkin comment.
2925
+** --revision|-r VERSION Commit from this version. Default is
2926
+** the checkout version (if available) or
2927
+** trunk (if used without a checkout).
2928
+** --wet-run Disables the default dry-run mode.
2929
+**
2930
+** Example:
2931
+**
2932
+** %fossil -m ... -r foo --as src/myfile.c myfile.c
2933
+**
28762934
*/
28772935
void test_ci_one_cmd(){
2878
- CheckinWebInfo cinf;
2879
- int parentVid = 0, newRid = 0;
2880
- const char * zFilename;
2881
- const char * zComment;
2882
- const char * zAsFilename;
2936
+ CheckinOneFileInfo cinf; /* checkin state */
2937
+ int parentVid = 0, newRid = 0; /* RID of parent version and new
2938
+ version */
2939
+ const char * zFilename; /* argv[2] */
2940
+ const char * zComment; /* -m comment */
2941
+ const char * zAsFilename; /* --as filename */
2942
+ const char * zRevision; /* --revision|-r [=trunk|checkout] */
28832943
int wetRunFlag = 0;
28842944
28852945
if(g.argc<3){
28862946
usage("INFILE");
28872947
}
2888
- db_find_and_open_repository(0, 0);
28892948
wetRunFlag = find_option("wet-run",0,0)!=0;
28902949
zComment = find_option("comment","m",1);
28912950
zAsFilename = find_option("as",0,1);
2951
+ zRevision = find_option("revision","r",1);
2952
+
2953
+ db_find_and_open_repository(0, 0);
28922954
verify_all_options();
28932955
user_select();
28942956
2957
+ if(1){
2958
+ char * zProjCode = db_get("project-code",0);
2959
+ assert(zProjCode);
2960
+ if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
2961
+ zProjCode)){
2962
+ fossil_fatal("Never, ever run this in/on the core fossil repo "
2963
+ "until it's been well-vetted. Use a temp/test "
2964
+ "repo.");
2965
+ }
2966
+ }
2967
+
28952968
zFilename = g.argv[2];
2896
- CheckinWebInfo_init(&cinf);
2969
+ CheckinOneFileInfo_init(&cinf);
28972970
blob_append(&cinf.comment,
28982971
zComment ? zComment : "This is a test comment.",
28992972
-1);
29002973
cinf.zFilename = mprintf("%s", zAsFilename ? zAsFilename : zFilename);
29012974
cinf.zUser = login_name();
29022975
2903
- cinf.pParent = manifest_get_by_name("trunk", &parentVid);
2976
+ if(zRevision==0 || zRevision[0]==0){
2977
+ if(g.localOpen/*checkout*/){
2978
+ zRevision = db_lget("checkout-hash", 0) /*leak*/;
2979
+ }else{
2980
+ zRevision = "trunk";
2981
+ }
2982
+ if(zRevision==0 || zRevision[0]==0){
2983
+ fossil_fatal("Cannot determine version to commit to.");
2984
+ }
2985
+ }
2986
+ cinf.pParent = manifest_get_by_name(zRevision, &parentVid);
29042987
assert(parentVid>0);
29052988
assert(cinf.pParent!=0);
29062989
blob_read_from_file(&cinf.fileContent, zFilename,
29072990
ExtFILE/*may want to reconsider*/);
29082991
sha3sum_init(256);
29092992
sha3sum_step_blob(&cinf.fileContent);
29102993
sha3sum_finish(&cinf.fileHash);
2911
- checkin_web(&cinf, &newRid, wetRunFlag ? 0 : 1);
2912
- CheckinWebInfo_cleanup(&cinf);
2994
+ checkin_one_file(&cinf, &newRid, wetRunFlag ? 0 : 1);
2995
+ CheckinOneFileInfo_cleanup(&cinf);
29132996
if(wetRunFlag!=0 && newRid!=0 && g.localOpen!=0){
29142997
fossil_warning("The checkout state is now out of sync "
29152998
"with regards to this commit. It needs to be "
29162999
"'update'd or 'close'd and re-'open'ed.");
2917
- /* vfile_check_signature(newRid,0); does not do the trick */
29183000
}
29193001
}
29203002
--- src/checkin.c
+++ src/checkin.c
@@ -2678,11 +2678,11 @@
2678 #if INTERFACE
2679 /*
2680 ** State for the "web-checkin" infrastructure, which enables the
2681 ** ability to commit changes to a single file via an HTTP request.
2682 */
2683 struct CheckinWebInfo {
2684 Blob comment; /* Check-in comment text */
2685 char *zMimetype; /* Mimetype of check-in command. May be NULL */
2686 Manifest * pParent; /* parent checkin */
2687 const char *zUser; /* User name */
2688 char *zFilename; /* Name of single file to commit. */
@@ -2689,53 +2689,65 @@
2689 Blob fileContent; /* Content of the modified file. */
2690 Blob fileHash; /* Hash of this->fileContent */
2691 };
2692 #endif /* INTERFACE */
2693
2694 static void CheckinWebInfo_init( CheckinWebInfo * p ){
2695 memset(p, 0, sizeof(struct CheckinWebInfo));
2696 p->comment = p->fileContent = p->fileHash = empty_blob;
2697 }
2698
2699 static void CheckinWebInfo_cleanup( CheckinWebInfo * p ){
2700 blob_reset(&p->comment);
2701 blob_reset(&p->fileContent);
2702 blob_reset(&p->fileHash);
2703 if(p->pParent){
2704 manifest_destroy(p->pParent);
2705 }
2706 fossil_free(p->zFilename);
2707 fossil_free(p->zMimetype);
2708 CheckinWebInfo_init(p);
2709 }
2710
2711 /*
2712 ** Creates a manifest file, written to pOut, from the state in the
2713 ** fully-populated pCI argument. On error, returns non-0 and, if
2714 ** pErr is not NULL, writes an error message there.
2715 */
2716 static int create_manifest_web( Blob * pOut, CheckinWebInfo * pCI,
2717 Blob * pErr){
2718 Blob zCard = empty_blob;
2719 ManifestFile *zFile;
2720 int cmp = -99;
2721 const char *zPerm = "";
2722 const char *zFilename = 0;
2723 const char *zUuid = 0;
2724 int (*fncmp)(char const *, char const *) =
 
 
2725 filenames_are_case_sensitive()
2726 ? fossil_strcmp
2727 : fossil_stricmp;
2728
2729 #define mf_err(MSG) if(pErr) blob_append(pErr,MSG,-1); return 1
 
 
 
 
 
2730 /* Potential TODOs include...
2731 ** - Create a delta manifest, if possible, rather than a baseline.
2732 ** - Enable adding of new files? It's implemented by disabled until
2733 ** we at least ensure that pCI->zFilename is a path-relative
2734 ** filename.
2735 ** - Maybe add support for tags. Those can be edited via /info page.
2736 ** - Check for a commit to a closed leaf and re-open it.
 
 
 
 
 
2737 */
2738 blob_zero(pOut);
2739 if(blob_size(&pCI->comment)!=0){
2740 blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
2741 }else{
@@ -2745,54 +2757,58 @@
2745
2746 manifest_file_rewind(pCI->pParent);
2747 while((zFile = manifest_file_next(pCI->pParent, 0))){
2748 cmp = fncmp(zFile->zName, pCI->zFilename);
2749 if(cmp<0){
2750 blob_appendf(pOut, "F %F %s%s\n", zFile->zName, zFile->zUuid,
2751 zFile->zPerm);
 
2752 }else{
2753 break;
2754 }
2755 }
2756 if(cmp==0){ /* Match */
2757 const int perm = manifest_file_mperm(zFile);
2758 if(PERM_LNK==perm){
2759 mf_err("Cannot commit symlinks with this approach.");
2760 }
2761 zPerm = PERM_EXE==perm ? " x" : "";
2762 zFilename = zFile->zName
2763 /* use original name in case of case difference */;
2764 zUuid = blob_str(&pCI->fileHash);
2765 }else{
2766 /* This is a new file. */
 
2767 zFilename = pCI->zFilename;
2768 zPerm = "";
2769 zUuid = blob_str(&pCI->fileHash);
2770 if(cmp>0){
2771 assert(zFile!=0);
2772 assert(pCI->pParent->iFile>0);
2773 --pCI->pParent->iFile
2774 /* so that the next step picks up that entry again */;
2775 }
 
 
 
 
 
 
 
2776 }
2777 assert(zFilename);
2778 assert(zUuid);
2779 assert(zPerm);
2780 blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
2781 while((zFile = manifest_file_next(pCI->pParent, 0))){
2782 cmp = fncmp(zFile->zName, pCI->zFilename);
2783 assert(cmp>0);
2784 if(cmp<=0){
2785 mf_err("Mis-ordering of F-cards.");
2786 }
2787 blob_appendf(pOut, "F %F %s%s%s\n",
2788 zFile->zName, zFile->zUuid,
2789 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2790 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2791 }
2792
2793 if(pCI->zMimetype){
2794 blob_appendf(pOut, "N %F\n", pCI->zMimetype);
2795 }
2796
2797 blob_appendf(pOut, "P %z\n",
2798 db_text(0,"SELECT uuid FROM blob WHERE rid=%d",
@@ -2800,120 +2816,186 @@
2800 blob_appendf(pOut, "U %F\n", pCI->zUser);
2801 md5sum_blob(pOut, &zCard);
2802 blob_appendf(pOut, "Z %b\n", &zCard);
2803 blob_reset(&zCard);
2804 return 0;
 
 
 
 
2805 #undef mf_err
2806 }
2807
2808 /*
2809 ** EXPERIMENTAL! A so-called "web checkin" is a slimmed-down form of
2810 ** the checkin command which accepts only a single file and is
2811 ** intended to accept edits to a file via the web interface.
 
 
 
2812 **
2813 ** This routine uses the state from the given fully-populated pCI
2814 ** argument to add pCI->fileContent to the database, and create and
2815 ** save a manifest for that change.
 
2816 **
2817 ** Fails fatally on error. If pRid is not NULL, the RID of the
2818 ** resulting manifest is written to *pRid. If bDryRun is true,
2819 ** it rolls back its transaction, else it commits as usual.
2820 **
2821 */
2822 void checkin_web( CheckinWebInfo * pCI, int *pRid, int bDryRun ){
2823 Blob mf = empty_blob;
2824 Blob err = empty_blob;
2825 int rid = 0, frid = 0;
2826 const int isPrivate = content_is_private(pCI->pParent->rid);
2827 ManifestFile * zFile;
 
 
2828
2829 db_begin_transaction();
2830 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
2831 fossil_fatal("no such user: %s", pCI->zUser);
2832 }
 
 
 
 
 
2833 manifest_file_rewind(pCI->pParent);
2834 zFile = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
2835 if(!zFile){
2836 fossil_fatal("File [%s] not found in manifest. "
2837 "Adding new files is currently not allowed.",
2838 pCI->zFilename);
 
 
 
 
 
 
 
2839 }
2840 if(create_manifest_web(&mf, pCI, &err)!=0){
2841 fossil_fatal("create_manifest_web() failed: %B", &err);
 
2842 }
 
2843 frid = content_put_ex(&pCI->fileContent, blob_str(&pCI->fileHash),
2844 0, 0, isPrivate);
2845 manifest_file_rewind(pCI->pParent);
2846 zFile = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
2847 if(zFile!=0){
2848 int prevFRid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q",
2849 zFile->zUuid);
2850 assert(prevFRid>0);
2851 content_deltify(frid, &prevFRid, 1, 0);
2852 }
 
2853 rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
2854 if(pRid!=0){
2855 *pRid = rid;
2856 }
 
 
2857 fossil_print("Manifest:\n%b", &mf);
 
2858 manifest_crosslink(rid, &mf, 0);
2859 if(bDryRun){
2860 fossil_print("Rolling back transaction.\n");
2861 }
2862 db_end_transaction(bDryRun ? 1 : 0);
2863 blob_reset(&mf);
2864 }
2865
2866 /*
2867 ** COMMAND: test-ci-web
2868 **
2869 ** This is an on-going experiment, subject to change or removal at
2870 ** any time.
2871 **
2872 ** Usage: %fossil ?OPTIONS? FILENAME
2873 **
2874 ** where FILENAME is a repo-relative name as it would appear in the
2875 ** vfile table.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2876 */
2877 void test_ci_one_cmd(){
2878 CheckinWebInfo cinf;
2879 int parentVid = 0, newRid = 0;
2880 const char * zFilename;
2881 const char * zComment;
2882 const char * zAsFilename;
 
 
2883 int wetRunFlag = 0;
2884
2885 if(g.argc<3){
2886 usage("INFILE");
2887 }
2888 db_find_and_open_repository(0, 0);
2889 wetRunFlag = find_option("wet-run",0,0)!=0;
2890 zComment = find_option("comment","m",1);
2891 zAsFilename = find_option("as",0,1);
 
 
 
2892 verify_all_options();
2893 user_select();
2894
 
 
 
 
 
 
 
 
 
 
 
2895 zFilename = g.argv[2];
2896 CheckinWebInfo_init(&cinf);
2897 blob_append(&cinf.comment,
2898 zComment ? zComment : "This is a test comment.",
2899 -1);
2900 cinf.zFilename = mprintf("%s", zAsFilename ? zAsFilename : zFilename);
2901 cinf.zUser = login_name();
2902
2903 cinf.pParent = manifest_get_by_name("trunk", &parentVid);
 
 
 
 
 
 
 
 
 
 
2904 assert(parentVid>0);
2905 assert(cinf.pParent!=0);
2906 blob_read_from_file(&cinf.fileContent, zFilename,
2907 ExtFILE/*may want to reconsider*/);
2908 sha3sum_init(256);
2909 sha3sum_step_blob(&cinf.fileContent);
2910 sha3sum_finish(&cinf.fileHash);
2911 checkin_web(&cinf, &newRid, wetRunFlag ? 0 : 1);
2912 CheckinWebInfo_cleanup(&cinf);
2913 if(wetRunFlag!=0 && newRid!=0 && g.localOpen!=0){
2914 fossil_warning("The checkout state is now out of sync "
2915 "with regards to this commit. It needs to be "
2916 "'update'd or 'close'd and re-'open'ed.");
2917 /* vfile_check_signature(newRid,0); does not do the trick */
2918 }
2919 }
2920
--- src/checkin.c
+++ src/checkin.c
@@ -2678,11 +2678,11 @@
2678 #if INTERFACE
2679 /*
2680 ** State for the "web-checkin" infrastructure, which enables the
2681 ** ability to commit changes to a single file via an HTTP request.
2682 */
2683 struct CheckinOneFileInfo {
2684 Blob comment; /* Check-in comment text */
2685 char *zMimetype; /* Mimetype of check-in command. May be NULL */
2686 Manifest * pParent; /* parent checkin */
2687 const char *zUser; /* User name */
2688 char *zFilename; /* Name of single file to commit. */
@@ -2689,53 +2689,65 @@
2689 Blob fileContent; /* Content of the modified file. */
2690 Blob fileHash; /* Hash of this->fileContent */
2691 };
2692 #endif /* INTERFACE */
2693
2694 static void CheckinOneFileInfo_init( CheckinOneFileInfo * p ){
2695 memset(p, 0, sizeof(struct CheckinOneFileInfo));
2696 p->comment = p->fileContent = p->fileHash = empty_blob;
2697 }
2698
2699 static void CheckinOneFileInfo_cleanup( CheckinOneFileInfo * p ){
2700 blob_reset(&p->comment);
2701 blob_reset(&p->fileContent);
2702 blob_reset(&p->fileHash);
2703 if(p->pParent){
2704 manifest_destroy(p->pParent);
2705 }
2706 fossil_free(p->zFilename);
2707 fossil_free(p->zMimetype);
2708 CheckinOneFileInfo_init(p);
2709 }
2710
2711 /*
2712 ** Creates a manifest file, written to pOut, from the state in the
2713 ** fully-populated pCI argument. Returns true on success. On error,
2714 ** returns 0 and, if pErr is not NULL, writes an error message there.
2715 */
2716 static int create_manifest_one_file( Blob * pOut, CheckinOneFileInfo * pCI,
2717 Blob * pErr){
2718 Blob zCard = empty_blob; /* Z-card checksum */
2719 ManifestFile *zFile; /* One file entry from the pCI->pParent */
2720 int cmp = -99; /* filename comparison result */
2721 int fperm = 0; /* file permissions */
2722 const char *zPerm = 0; /* permissions for new F-card */
2723 const char *zFilename = 0; /* filename to use for F-card */
2724 const char *zUuid = 0; /* UUID for F-card */
2725 const char *zErrMsg = 0; /* For error reporting. */
2726 int (*fncmp)(char const *, char const *) = /* filename comparator */
2727 filenames_are_case_sensitive()
2728 ? fossil_strcmp
2729 : fossil_stricmp;
2730
2731 assert(blob_str(&pCI->fileHash));
2732 assert(pCI->pParent);
2733 assert(pCI->zFilename);
2734 assert(pCI->zUser);
2735
2736 #define mf_err(MSG) zErrMsg = MSG; goto manifest_error
2737 /* Potential TODOs include...
2738 ** - Create a delta manifest, if possible, rather than a baseline.
2739 ** - Enable adding of new files? It's implemented by disabled until
2740 ** we at least ensure that pCI->zFilename is a path-relative
2741 ** filename.
2742 ** - Maybe add support for tags. Those can be edited via /info page.
2743 ** - Check for a commit to a closed leaf and re-open it.
2744 ** - Symlinks: if we're really in a checkout, handle commit/add of
2745 ** symlinks like a normal commit would. For now we bail out if
2746 ** told to handle a symlink because there would seem to be no(?)
2747 ** sensible way to handle a symlink add/checkin without a
2748 ** checkout.
2749 */
2750 blob_zero(pOut);
2751 if(blob_size(&pCI->comment)!=0){
2752 blob_appendf(pOut, "C %F\n", blob_str(&pCI->comment));
2753 }else{
@@ -2745,54 +2757,58 @@
2757
2758 manifest_file_rewind(pCI->pParent);
2759 while((zFile = manifest_file_next(pCI->pParent, 0))){
2760 cmp = fncmp(zFile->zName, pCI->zFilename);
2761 if(cmp<0){
2762 blob_appendf(pOut, "F %F %s%s%s\n", zFile->zName, zFile->zUuid,
2763 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2764 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2765 }else{
2766 break;
2767 }
2768 }
2769 if(cmp==0){ /* Match */
2770 fperm = manifest_file_mperm(zFile);
 
 
 
 
2771 zFilename = zFile->zName
2772 /* use original name in case of name-case difference */;
2773 zUuid = blob_str(&pCI->fileHash);
2774 }else{
2775 /* This is a new file. */
2776 fperm = file_perm(pCI->zFilename, ExtFILE);
2777 zFilename = pCI->zFilename;
 
2778 zUuid = blob_str(&pCI->fileHash);
2779 if(cmp>0){
2780 assert(zFile!=0);
2781 assert(pCI->pParent->iFile>0);
2782 --pCI->pParent->iFile
2783 /* so that the next step picks up that entry again */;
2784 }
2785 }
2786 if(PERM_LNK==fperm){
2787 mf_err("Cannot commit symlinks with this approach.");
2788 }else if(PERM_EXE==fperm){
2789 zPerm = " x";
2790 }else{
2791 zPerm = "";
2792 }
2793 assert(zFilename);
2794 assert(zUuid);
2795 assert(zPerm);
2796 blob_appendf(pOut, "F %F %s%s\n", zFilename, zUuid, zPerm);
2797 while((zFile = manifest_file_next(pCI->pParent, 0))){
2798 cmp = fncmp(zFile->zName, pCI->zFilename);
2799 assert(cmp>0);
2800 if(cmp<=0){
2801 mf_err("Internal error: mis-ordering of F-cards detected.");
2802 }
2803 blob_appendf(pOut, "F %F %s%s%s\n",
2804 zFile->zName, zFile->zUuid,
2805 (zFile->zPerm && *zFile->zPerm) ? " " : "",
2806 (zFile->zPerm && *zFile->zPerm) ? zFile->zPerm : "");
2807 }
2808
2809 if(pCI->zMimetype!=0 && pCI->zMimetype[0]!=0){
2810 blob_appendf(pOut, "N %F\n", pCI->zMimetype);
2811 }
2812
2813 blob_appendf(pOut, "P %z\n",
2814 db_text(0,"SELECT uuid FROM blob WHERE rid=%d",
@@ -2800,120 +2816,186 @@
2816 blob_appendf(pOut, "U %F\n", pCI->zUser);
2817 md5sum_blob(pOut, &zCard);
2818 blob_appendf(pOut, "Z %b\n", &zCard);
2819 blob_reset(&zCard);
2820 return 0;
2821 manifest_error:
2822 assert( zErrMsg );
2823 if(pErr) blob_append(pErr,zErrMsg,-1);
2824 return 1;
2825 #undef mf_err
2826 }
2827
2828 /*
2829 ** EXPERIMENTAL! Subject to change or removal at any time.
2830 **
2831 ** A so-called "single-file checkin" is a slimmed-down form of the
2832 ** checkin command which accepts only a single file and is intended to
2833 ** accept edits to a file via the web interface or from the CLI from
2834 ** outside of a checkout.
2835 **
2836 ** This routine uses the state from the given fully-populated pCI
2837 ** argument to add pCI->fileContent to the database, and create and
2838 ** save a manifest for that change. Ownership of pCI and its contents
2839 ** are unchanged.
2840 **
2841 ** Fails fatally on error. If pRid is not NULL, the RID of the
2842 ** resulting manifest is written to *pRid. If bDryRun is true,
2843 ** it rolls back its transaction, else it commits as usual.
 
2844 */
2845 void checkin_one_file( CheckinOneFileInfo * pCI, int *pRid, int bDryRun ){
2846 Blob mf = empty_blob;
2847 Blob err = empty_blob;
2848 int rid = 0, frid = 0;
2849 const int isPrivate = content_is_private(pCI->pParent->rid);
2850 ManifestFile * zFile;
2851 const char * zFilePrevUuid = 0; /* UUID of previous version of
2852 the file */
2853
2854 db_begin_transaction();
2855 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", pCI->zUser) ){
2856 fossil_fatal("no such user: %s", pCI->zUser);
2857 }
2858 /*
2859 ** Confirm that pCI->zFilename can be found in pCI->pParent.
2860 ** If not, fail. This is admittedly an artificial limitation,
2861 ** not strictly necessary.
2862 */
2863 manifest_file_rewind(pCI->pParent);
2864 zFile = manifest_file_seek(pCI->pParent, pCI->zFilename, 0);
2865 if(!zFile){
2866 fossil_fatal("File [%s] not found in manifest. "
2867 "Adding new files is currently not allowed.",
2868 pCI->zFilename);
2869 }else if(zFile->zPerm && strstr(zFile->zPerm, "l")){
2870 fossil_fatal("Cannot save a symlink this way.");
2871 }else{
2872 if(0==fossil_strcmp(zFile->zUuid, blob_str(&pCI->fileHash))){
2873 fossil_fatal("File is unchanged. Not saving.");
2874 }
2875 zFilePrevUuid = zFile->zUuid;
2876 }
2877 /* Create the manifest... */
2878 if(create_manifest_one_file(&mf, pCI, &err)==0){
2879 fossil_fatal("create_manifest_one_file() failed: %B", &err);
2880 }
2881 /* Add the file content to the db... */
2882 frid = content_put_ex(&pCI->fileContent, blob_str(&pCI->fileHash),
2883 0, 0, isPrivate);
2884 if(zFilePrevUuid!=0){
2885 /* Deltify against previous file version... */
 
2886 int prevFRid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q",
2887 zFilePrevUuid);
2888 assert(prevFRid>0);
2889 content_deltify(frid, &prevFRid, 1, 0);
2890 }
2891 /* Save and crosslink the manifest... */
2892 rid = content_put_ex(&mf, 0, 0, 0, isPrivate);
2893 if(pRid!=0){
2894 *pRid = rid;
2895 }
2896 #if 1
2897 /* Only for development/debugging... */
2898 fossil_print("Manifest:\n%b", &mf);
2899 #endif
2900 manifest_crosslink(rid, &mf, 0);
2901 if(bDryRun){
2902 fossil_print("Rolling back transaction.\n");
2903 }
2904 db_end_transaction(bDryRun ? 1 : 0);
2905 blob_reset(&mf);
2906 }
2907
2908 /*
2909 ** COMMAND: test-ci-one
2910 **
2911 ** This is an on-going experiment, subject to change or removal at
2912 ** any time.
2913 **
2914 ** Usage: %fossil ?OPTIONS? FILENAME
2915 **
2916 ** where FILENAME is a repo-relative name as it would appear in the
2917 ** vfile table.
2918 **
2919 ** Options:
2920 **
2921 ** --as FILENAME The repository-side name of the input file,
2922 ** relative to the top of the repostory.
2923 ** Default is the same as the input file name.
2924 ** --comment|-m COMMENT Optional checkin comment.
2925 ** --revision|-r VERSION Commit from this version. Default is
2926 ** the checkout version (if available) or
2927 ** trunk (if used without a checkout).
2928 ** --wet-run Disables the default dry-run mode.
2929 **
2930 ** Example:
2931 **
2932 ** %fossil -m ... -r foo --as src/myfile.c myfile.c
2933 **
2934 */
2935 void test_ci_one_cmd(){
2936 CheckinOneFileInfo cinf; /* checkin state */
2937 int parentVid = 0, newRid = 0; /* RID of parent version and new
2938 version */
2939 const char * zFilename; /* argv[2] */
2940 const char * zComment; /* -m comment */
2941 const char * zAsFilename; /* --as filename */
2942 const char * zRevision; /* --revision|-r [=trunk|checkout] */
2943 int wetRunFlag = 0;
2944
2945 if(g.argc<3){
2946 usage("INFILE");
2947 }
 
2948 wetRunFlag = find_option("wet-run",0,0)!=0;
2949 zComment = find_option("comment","m",1);
2950 zAsFilename = find_option("as",0,1);
2951 zRevision = find_option("revision","r",1);
2952
2953 db_find_and_open_repository(0, 0);
2954 verify_all_options();
2955 user_select();
2956
2957 if(1){
2958 char * zProjCode = db_get("project-code",0);
2959 assert(zProjCode);
2960 if(0==fossil_stricmp("CE59BB9F186226D80E49D1FA2DB29F935CCA0333",
2961 zProjCode)){
2962 fossil_fatal("Never, ever run this in/on the core fossil repo "
2963 "until it's been well-vetted. Use a temp/test "
2964 "repo.");
2965 }
2966 }
2967
2968 zFilename = g.argv[2];
2969 CheckinOneFileInfo_init(&cinf);
2970 blob_append(&cinf.comment,
2971 zComment ? zComment : "This is a test comment.",
2972 -1);
2973 cinf.zFilename = mprintf("%s", zAsFilename ? zAsFilename : zFilename);
2974 cinf.zUser = login_name();
2975
2976 if(zRevision==0 || zRevision[0]==0){
2977 if(g.localOpen/*checkout*/){
2978 zRevision = db_lget("checkout-hash", 0) /*leak*/;
2979 }else{
2980 zRevision = "trunk";
2981 }
2982 if(zRevision==0 || zRevision[0]==0){
2983 fossil_fatal("Cannot determine version to commit to.");
2984 }
2985 }
2986 cinf.pParent = manifest_get_by_name(zRevision, &parentVid);
2987 assert(parentVid>0);
2988 assert(cinf.pParent!=0);
2989 blob_read_from_file(&cinf.fileContent, zFilename,
2990 ExtFILE/*may want to reconsider*/);
2991 sha3sum_init(256);
2992 sha3sum_step_blob(&cinf.fileContent);
2993 sha3sum_finish(&cinf.fileHash);
2994 checkin_one_file(&cinf, &newRid, wetRunFlag ? 0 : 1);
2995 CheckinOneFileInfo_cleanup(&cinf);
2996 if(wetRunFlag!=0 && newRid!=0 && g.localOpen!=0){
2997 fossil_warning("The checkout state is now out of sync "
2998 "with regards to this commit. It needs to be "
2999 "'update'd or 'close'd and re-'open'ed.");
 
3000 }
3001 }
3002

Keyboard Shortcuts

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