Fossil SCM

Record information about merge operations in the localdb.mergestat table. Make that information available using the merge-info command.

drh 2024-12-03 18:13 merge-enhancements
Commit 625ff9d574307c3eb31b05fe95df81b178a4ee34fb07f883e48f6c5f03f98e97
1 file changed +173 -6
+173 -6
--- src/merge.c
+++ src/merge.c
@@ -19,10 +19,87 @@
1919
** a single tree.
2020
*/
2121
#include "config.h"
2222
#include "merge.h"
2323
#include <assert.h>
24
+
25
+/*
26
+** COMMAND: merge-info
27
+**
28
+** Display information about the most recent merge operation.
29
+**
30
+** Right now, this command basically just dumps the localdb.mergestat
31
+** table. The plan moving forward is that it can generate data for
32
+** a Tk-based GUI to show the details of the merge. This command is
33
+** a work-in-progress.
34
+*/
35
+void merge_info_cmd(void){
36
+ Stmt q;
37
+ verify_all_options();
38
+ db_must_be_within_tree();
39
+
40
+ if( !db_table_exists("localdb","mergestat") ){
41
+ return;
42
+ }
43
+ db_prepare(&q,
44
+ /* 0 1 2 3 4 */
45
+ "SELECT op, fn, fnr, nc, msg FROM mergestat ORDER BY coalesce(fnr,fn)"
46
+ );
47
+ while( db_step(&q)==SQLITE_ROW ){
48
+ const char *zOp = db_column_text(&q, 0);
49
+ const char *zName = db_column_text(&q, 2);
50
+ const char *zErr = db_column_text(&q, 4);
51
+ if( zName==0 ) zName = db_column_text(&q, 1);
52
+ if( zErr ){
53
+ fossil_print("%-7s %s ** %s **\n", zOp, zName, zErr);
54
+ }else{
55
+ fossil_print("%-7s %s\n", zOp, zName);
56
+ }
57
+ }
58
+ db_finalize(&q);
59
+}
60
+
61
+/*
62
+** Erase all information about prior merges. Do this, for example, after
63
+** a commit.
64
+*/
65
+void merge_info_forget(void){
66
+ db_multi_exec("DROP TABLE IF EXISTS localdb.mergestat");
67
+}
68
+
69
+
70
+/*
71
+** Initialize the MERGESTAT table.
72
+**
73
+** Notes about mergestat:
74
+**
75
+** * ridv is a positive integer and sz is NULL if the V file contained
76
+** no local edits prior to the merge. If the V file was modified prior
77
+** to the merge then ridv is NULL and sz is the size of the file prior
78
+** to merge.
79
+**
80
+** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
81
+** added by merge.
82
+*/
83
+void merge_info_init(void){
84
+ db_multi_exec(
85
+ "DROP TABLE IF EXISTS localdb.mergestat;\n"
86
+ "CREATE TABLE localdb.mergestat(\n"
87
+ " op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
88
+ " fnp TEXT, -- Name of the pivot file (P)\n"
89
+ " ridp INT, -- RID for the pivot file\n"
90
+ " fn TEXT, -- Name of origin file (V)\n"
91
+ " ridv INT, -- RID for origin file, or NULL if previously edited\n"
92
+ " sz INT, -- Size of origin file in bytes, NULL if unedited\n"
93
+ " fnm TEXT, -- Name of the file being merged in (M)\n"
94
+ " ridm INT, -- RID for the merge-in file\n"
95
+ " fnr TEXT, -- Name of the final output file, after all renaming\n"
96
+ " nc INT DEFAULT 0, -- Number of conflicts\n"
97
+ " msg TEXT -- Error message\n"
98
+ ");"
99
+ );
100
+}
24101
25102
/*
26103
** Print information about a particular check-in.
27104
*/
28105
void print_checkin_description(int rid, int indent, const char *zLabel){
@@ -323,11 +400,11 @@
323400
** --force-missing Force the merge even if there is missing content
324401
** --integrate Merged branch will be closed when committing
325402
** -K|--keep-merge-files On merge conflict, retain the temporary files
326403
** used for merging, named *-baseline, *-original,
327404
** and *-merge.
328
-** -n|--dry-run If given, display instead of run actions
405
+** -n|--dry-run Do not actually change files on disk
329406
** --nosync Do not auto-sync prior to merging
330407
** -v|--verbose Show additional details of the merge
331408
*/
332409
void merge_cmd(void){
333410
int vid; /* Current version "V" */
@@ -800,15 +877,21 @@
800877
801878
/************************************************************************
802879
** All of the information needed to do the merge is now contained in the
803880
** FV table. Starting here, we begin to actually carry out the merge.
804881
**
805
- ** First, find files that have changed from P->M but not P->V.
882
+ ** Begin by constructing the localdb.mergestat table.
883
+ */
884
+ merge_info_init();
885
+
886
+ /*
887
+ ** Find files that have changed from P->M but not P->V.
806888
** Copy the M content over into V.
807889
*/
808890
db_prepare(&q,
809
- "SELECT idv, ridm, fn, islinkm FROM fv"
891
+ /* 0 1 2 3 4 5 6 7 */
892
+ "SELECT idv, ridm, fn, islinkm, fnp, ridp, ridv, fnm FROM fv"
810893
" WHERE idp>0 AND idv>0 AND idm>0"
811894
" AND ridm!=ridp AND ridv=ridp AND NOT chnged"
812895
);
813896
while( db_step(&q)==SQLITE_ROW ){
814897
int idv = db_column_int(&q, 0);
@@ -825,10 +908,21 @@
825908
" THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
826909
" WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
827910
);
828911
vfile_to_disk(0, idv, 0, 0);
829912
}
913
+ db_multi_exec(
914
+ "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr)"
915
+ "VALUES('UPDATE',%Q,%d,%Q,%d,%Q,%d,%Q)",
916
+ /* fnp */ db_column_text(&q, 4),
917
+ /* ridp */ db_column_int(&q,5),
918
+ /* fn */ zName,
919
+ /* ridv */ db_column_int(&q,6),
920
+ /* fnm */ db_column_text(&q, 7),
921
+ /* ridm */ ridm,
922
+ /* fnr */ zName
923
+ );
830924
}
831925
db_finalize(&q);
832926
833927
/*
834928
** Do a three-way merge on files that have changes on both P->M and P->V.
@@ -836,11 +930,15 @@
836930
** Proceed even if the file doesn't exist on P, just like the common ancestor
837931
** of M and V is an empty file. In this case, merge conflict marks will be
838932
** added to the file and user will be forced to take a decision.
839933
*/
840934
db_prepare(&q,
841
- "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv"
935
+ /* 0 1 2 3 4 5 6 7 8 */
936
+ "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm,"
937
+ /* 9 10 11 */
938
+ " fnp, fnm, chnged"
939
+ " FROM fv"
842940
" WHERE idv>0 AND idm>0"
843941
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
844942
glob_expr("fv.fn", zBinGlob)
845943
);
846944
while( db_step(&q)==SQLITE_ROW ){
@@ -851,10 +949,11 @@
851949
int isBinary = db_column_int(&q, 4);
852950
const char *zName = db_column_text(&q, 5);
853951
int isExe = db_column_int(&q, 6);
854952
int islinkv = db_column_int(&q, 7);
855953
int islinkm = db_column_int(&q, 8);
954
+ int chnged = db_column_int(&q, 11);
856955
int rc;
857956
char *zFullPath;
858957
Blob m, p, r;
859958
/* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
860959
if( verboseFlag ){
@@ -864,13 +963,29 @@
864963
fossil_print("MERGE %s\n", zName);
865964
}
866965
if( islinkv || islinkm ){
867966
fossil_print("***** Cannot merge symlink %s\n", zName);
868967
nConflict++;
968
+ db_multi_exec(
969
+ "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr,nc,msg)"
970
+ "VALUES('MERGE',%Q,%d,%Q,%d,%Q,%d,%Q,1,'cannot merge symlink')",
971
+ /* fnp */ db_column_text(&q, 9),
972
+ /* ridp */ ridp,
973
+ /* fn */ zName,
974
+ /* ridv */ ridv,
975
+ /* fnm */ db_column_text(&q, 10),
976
+ /* ridm */ ridm,
977
+ /* fnr */ zName
978
+ );
869979
}else{
980
+ i64 sz;
981
+ const char *zErrMsg = 0;
982
+ int nc = 0;
983
+
870984
if( !dryRunFlag ) undo_save(zName);
871985
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
986
+ sz = file_size(zFullPath, ExtFILE);
872987
content_get(ridp, &p);
873988
content_get(ridm, &m);
874989
if( isBinary ){
875990
rc = -1;
876991
blob_zero(&r);
@@ -887,15 +1002,35 @@
8871002
db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
8881003
if( rc>0 ){
8891004
fossil_print("***** %d merge conflict%s in %s\n",
8901005
rc, rc>1 ? "s" : "", zName);
8911006
nConflict++;
1007
+ nc = rc;
1008
+ zErrMsg = "merge conflicts";
8921009
}
8931010
}else{
8941011
fossil_print("***** Cannot merge binary file %s\n", zName);
8951012
nConflict++;
1013
+ nc = 1;
1014
+ zErrMsg = "cannot merge binary file";
8961015
}
1016
+ db_multi_exec(
1017
+ "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
1018
+ "VALUES('MERGE',%Q,%d,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL),%Q,%d,"
1019
+ "%Q,%d,%Q)",
1020
+ /* fnp */ db_column_text(&q, 9),
1021
+ /* ridp */ ridp,
1022
+ /* fn */ zName,
1023
+ /* ridv */ chnged==0, ridv,
1024
+ /* sz */ chnged!=0, sz,
1025
+ /* fnm */ db_column_text(&q, 10),
1026
+ /* ridm */ ridm,
1027
+ /* fnr */ zName,
1028
+ /* nc */ nc,
1029
+ /* msg */ zErrMsg
1030
+ );
1031
+ fossil_free(zFullPath);
8971032
blob_reset(&p);
8981033
blob_reset(&m);
8991034
blob_reset(&r);
9001035
}
9011036
vmerge_insert(idv, ridm);
@@ -904,22 +1039,33 @@
9041039
9051040
/*
9061041
** Drop files that are in P and V but not in M
9071042
*/
9081043
db_prepare(&q,
909
- "SELECT idv, fn, chnged FROM fv"
1044
+ "SELECT idv, fn, chnged, ridv FROM fv"
9101045
" WHERE idp>0 AND idv>0 AND idm=0"
9111046
);
9121047
while( db_step(&q)==SQLITE_ROW ){
9131048
int idv = db_column_int(&q, 0);
9141049
const char *zName = db_column_text(&q, 1);
9151050
int chnged = db_column_int(&q, 2);
1051
+ int ridv = db_column_int(&q, 3);
1052
+ int sz = -1;
1053
+ const char *zErrMsg = 0;
1054
+ int nc = 0;
9161055
/* Delete the file idv */
9171056
fossil_print("DELETE %s\n", zName);
9181057
if( chnged ){
1058
+ char *zFullPath;
9191059
fossil_warning("WARNING: local edits lost for %s", zName);
9201060
nConflict++;
1061
+ ridv = 0;
1062
+ nc = 1;
1063
+ zErrMsg = "local edits lost";
1064
+ zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1065
+ sz = file_size(zFullPath, ExtFILE);
1066
+ fossil_free(zFullPath);
9211067
}
9221068
if( !dryRunFlag ) undo_save(zName);
9231069
db_multi_exec(
9241070
"UPDATE vfile SET deleted=1 WHERE id=%d", idv
9251071
);
@@ -926,10 +1072,20 @@
9261072
if( !dryRunFlag ){
9271073
char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
9281074
file_delete(zFullPath);
9291075
free(zFullPath);
9301076
}
1077
+ db_multi_exec(
1078
+ "INSERT INTO localdb.mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,nc,msg)"
1079
+ "VALUES('DELETE',NULL,NULL,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL),"
1080
+ "NULL,NULL,%d,%Q)",
1081
+ /* fn */ zName,
1082
+ /* ridv */ chnged==0, ridv,
1083
+ /* sz */ chnged!=0, sz,
1084
+ /* nc */ nc,
1085
+ /* msg */ zErrMsg
1086
+ );
9311087
}
9321088
db_finalize(&q);
9331089
9341090
/* For certain sets of renames (e.g. A -> B and B -> A), a file that is
9351091
** being renamed must first be moved to a temporary location to avoid
@@ -956,10 +1112,14 @@
9561112
const char *zNewName = db_column_text(&q, 2);
9571113
int isExe = db_column_int(&q, 3);
9581114
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
9591115
if( !dryRunFlag ) undo_save(zOldName);
9601116
if( !dryRunFlag ) undo_save(zNewName);
1117
+ db_multi_exec(
1118
+ "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
1119
+ zOldName
1120
+ );
9611121
db_multi_exec(
9621122
"UPDATE vfile SET pathname=NULL, origname=pathname"
9631123
" WHERE vid=%d AND pathname=%Q;"
9641124
"UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
9651125
" WHERE id=%d;",
@@ -1009,11 +1169,11 @@
10091169
10101170
/*
10111171
** Insert into V any files that are not in V or P but are in M.
10121172
*/
10131173
db_prepare(&q,
1014
- "SELECT idm, fnm FROM fv"
1174
+ "SELECT idm, fnm, ridm FROM fv"
10151175
" WHERE idp=0 AND idv=0 AND idm>0"
10161176
);
10171177
while( db_step(&q)==SQLITE_ROW ){
10181178
int idm = db_column_int(&q, 0);
10191179
const char *zName;
@@ -1042,10 +1202,17 @@
10421202
nOverwrite++;
10431203
}else{
10441204
fossil_print("ADDED %s\n", zName);
10451205
}
10461206
fossil_free(zFullName);
1207
+ db_multi_exec(
1208
+ "INSERT INTO mergestat(op,fnm,ridm,fnr)"
1209
+ "VALUES('ADDED',%Q,%d,%Q)",
1210
+ /* fnm */ zName,
1211
+ /* ridm */ db_column_int(&q,2),
1212
+ /* fnr */ zName
1213
+ );
10471214
if( !dryRunFlag ){
10481215
undo_save(zName);
10491216
vfile_to_disk(0, idm, 0, 0);
10501217
}
10511218
}
10521219
--- src/merge.c
+++ src/merge.c
@@ -19,10 +19,87 @@
19 ** a single tree.
20 */
21 #include "config.h"
22 #include "merge.h"
23 #include <assert.h>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
25 /*
26 ** Print information about a particular check-in.
27 */
28 void print_checkin_description(int rid, int indent, const char *zLabel){
@@ -323,11 +400,11 @@
323 ** --force-missing Force the merge even if there is missing content
324 ** --integrate Merged branch will be closed when committing
325 ** -K|--keep-merge-files On merge conflict, retain the temporary files
326 ** used for merging, named *-baseline, *-original,
327 ** and *-merge.
328 ** -n|--dry-run If given, display instead of run actions
329 ** --nosync Do not auto-sync prior to merging
330 ** -v|--verbose Show additional details of the merge
331 */
332 void merge_cmd(void){
333 int vid; /* Current version "V" */
@@ -800,15 +877,21 @@
800
801 /************************************************************************
802 ** All of the information needed to do the merge is now contained in the
803 ** FV table. Starting here, we begin to actually carry out the merge.
804 **
805 ** First, find files that have changed from P->M but not P->V.
 
 
 
 
 
806 ** Copy the M content over into V.
807 */
808 db_prepare(&q,
809 "SELECT idv, ridm, fn, islinkm FROM fv"
 
810 " WHERE idp>0 AND idv>0 AND idm>0"
811 " AND ridm!=ridp AND ridv=ridp AND NOT chnged"
812 );
813 while( db_step(&q)==SQLITE_ROW ){
814 int idv = db_column_int(&q, 0);
@@ -825,10 +908,21 @@
825 " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
826 " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
827 );
828 vfile_to_disk(0, idv, 0, 0);
829 }
 
 
 
 
 
 
 
 
 
 
 
830 }
831 db_finalize(&q);
832
833 /*
834 ** Do a three-way merge on files that have changes on both P->M and P->V.
@@ -836,11 +930,15 @@
836 ** Proceed even if the file doesn't exist on P, just like the common ancestor
837 ** of M and V is an empty file. In this case, merge conflict marks will be
838 ** added to the file and user will be forced to take a decision.
839 */
840 db_prepare(&q,
841 "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv"
 
 
 
 
842 " WHERE idv>0 AND idm>0"
843 " AND ridm!=ridp AND (ridv!=ridp OR chnged)",
844 glob_expr("fv.fn", zBinGlob)
845 );
846 while( db_step(&q)==SQLITE_ROW ){
@@ -851,10 +949,11 @@
851 int isBinary = db_column_int(&q, 4);
852 const char *zName = db_column_text(&q, 5);
853 int isExe = db_column_int(&q, 6);
854 int islinkv = db_column_int(&q, 7);
855 int islinkm = db_column_int(&q, 8);
 
856 int rc;
857 char *zFullPath;
858 Blob m, p, r;
859 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
860 if( verboseFlag ){
@@ -864,13 +963,29 @@
864 fossil_print("MERGE %s\n", zName);
865 }
866 if( islinkv || islinkm ){
867 fossil_print("***** Cannot merge symlink %s\n", zName);
868 nConflict++;
 
 
 
 
 
 
 
 
 
 
 
869 }else{
 
 
 
 
870 if( !dryRunFlag ) undo_save(zName);
871 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
 
872 content_get(ridp, &p);
873 content_get(ridm, &m);
874 if( isBinary ){
875 rc = -1;
876 blob_zero(&r);
@@ -887,15 +1002,35 @@
887 db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
888 if( rc>0 ){
889 fossil_print("***** %d merge conflict%s in %s\n",
890 rc, rc>1 ? "s" : "", zName);
891 nConflict++;
 
 
892 }
893 }else{
894 fossil_print("***** Cannot merge binary file %s\n", zName);
895 nConflict++;
 
 
896 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897 blob_reset(&p);
898 blob_reset(&m);
899 blob_reset(&r);
900 }
901 vmerge_insert(idv, ridm);
@@ -904,22 +1039,33 @@
904
905 /*
906 ** Drop files that are in P and V but not in M
907 */
908 db_prepare(&q,
909 "SELECT idv, fn, chnged FROM fv"
910 " WHERE idp>0 AND idv>0 AND idm=0"
911 );
912 while( db_step(&q)==SQLITE_ROW ){
913 int idv = db_column_int(&q, 0);
914 const char *zName = db_column_text(&q, 1);
915 int chnged = db_column_int(&q, 2);
 
 
 
 
916 /* Delete the file idv */
917 fossil_print("DELETE %s\n", zName);
918 if( chnged ){
 
919 fossil_warning("WARNING: local edits lost for %s", zName);
920 nConflict++;
 
 
 
 
 
 
921 }
922 if( !dryRunFlag ) undo_save(zName);
923 db_multi_exec(
924 "UPDATE vfile SET deleted=1 WHERE id=%d", idv
925 );
@@ -926,10 +1072,20 @@
926 if( !dryRunFlag ){
927 char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
928 file_delete(zFullPath);
929 free(zFullPath);
930 }
 
 
 
 
 
 
 
 
 
 
931 }
932 db_finalize(&q);
933
934 /* For certain sets of renames (e.g. A -> B and B -> A), a file that is
935 ** being renamed must first be moved to a temporary location to avoid
@@ -956,10 +1112,14 @@
956 const char *zNewName = db_column_text(&q, 2);
957 int isExe = db_column_int(&q, 3);
958 fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
959 if( !dryRunFlag ) undo_save(zOldName);
960 if( !dryRunFlag ) undo_save(zNewName);
 
 
 
 
961 db_multi_exec(
962 "UPDATE vfile SET pathname=NULL, origname=pathname"
963 " WHERE vid=%d AND pathname=%Q;"
964 "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
965 " WHERE id=%d;",
@@ -1009,11 +1169,11 @@
1009
1010 /*
1011 ** Insert into V any files that are not in V or P but are in M.
1012 */
1013 db_prepare(&q,
1014 "SELECT idm, fnm FROM fv"
1015 " WHERE idp=0 AND idv=0 AND idm>0"
1016 );
1017 while( db_step(&q)==SQLITE_ROW ){
1018 int idm = db_column_int(&q, 0);
1019 const char *zName;
@@ -1042,10 +1202,17 @@
1042 nOverwrite++;
1043 }else{
1044 fossil_print("ADDED %s\n", zName);
1045 }
1046 fossil_free(zFullName);
 
 
 
 
 
 
 
1047 if( !dryRunFlag ){
1048 undo_save(zName);
1049 vfile_to_disk(0, idm, 0, 0);
1050 }
1051 }
1052
--- src/merge.c
+++ src/merge.c
@@ -19,10 +19,87 @@
19 ** a single tree.
20 */
21 #include "config.h"
22 #include "merge.h"
23 #include <assert.h>
24
25 /*
26 ** COMMAND: merge-info
27 **
28 ** Display information about the most recent merge operation.
29 **
30 ** Right now, this command basically just dumps the localdb.mergestat
31 ** table. The plan moving forward is that it can generate data for
32 ** a Tk-based GUI to show the details of the merge. This command is
33 ** a work-in-progress.
34 */
35 void merge_info_cmd(void){
36 Stmt q;
37 verify_all_options();
38 db_must_be_within_tree();
39
40 if( !db_table_exists("localdb","mergestat") ){
41 return;
42 }
43 db_prepare(&q,
44 /* 0 1 2 3 4 */
45 "SELECT op, fn, fnr, nc, msg FROM mergestat ORDER BY coalesce(fnr,fn)"
46 );
47 while( db_step(&q)==SQLITE_ROW ){
48 const char *zOp = db_column_text(&q, 0);
49 const char *zName = db_column_text(&q, 2);
50 const char *zErr = db_column_text(&q, 4);
51 if( zName==0 ) zName = db_column_text(&q, 1);
52 if( zErr ){
53 fossil_print("%-7s %s ** %s **\n", zOp, zName, zErr);
54 }else{
55 fossil_print("%-7s %s\n", zOp, zName);
56 }
57 }
58 db_finalize(&q);
59 }
60
61 /*
62 ** Erase all information about prior merges. Do this, for example, after
63 ** a commit.
64 */
65 void merge_info_forget(void){
66 db_multi_exec("DROP TABLE IF EXISTS localdb.mergestat");
67 }
68
69
70 /*
71 ** Initialize the MERGESTAT table.
72 **
73 ** Notes about mergestat:
74 **
75 ** * ridv is a positive integer and sz is NULL if the V file contained
76 ** no local edits prior to the merge. If the V file was modified prior
77 ** to the merge then ridv is NULL and sz is the size of the file prior
78 ** to merge.
79 **
80 ** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
81 ** added by merge.
82 */
83 void merge_info_init(void){
84 db_multi_exec(
85 "DROP TABLE IF EXISTS localdb.mergestat;\n"
86 "CREATE TABLE localdb.mergestat(\n"
87 " op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
88 " fnp TEXT, -- Name of the pivot file (P)\n"
89 " ridp INT, -- RID for the pivot file\n"
90 " fn TEXT, -- Name of origin file (V)\n"
91 " ridv INT, -- RID for origin file, or NULL if previously edited\n"
92 " sz INT, -- Size of origin file in bytes, NULL if unedited\n"
93 " fnm TEXT, -- Name of the file being merged in (M)\n"
94 " ridm INT, -- RID for the merge-in file\n"
95 " fnr TEXT, -- Name of the final output file, after all renaming\n"
96 " nc INT DEFAULT 0, -- Number of conflicts\n"
97 " msg TEXT -- Error message\n"
98 ");"
99 );
100 }
101
102 /*
103 ** Print information about a particular check-in.
104 */
105 void print_checkin_description(int rid, int indent, const char *zLabel){
@@ -323,11 +400,11 @@
400 ** --force-missing Force the merge even if there is missing content
401 ** --integrate Merged branch will be closed when committing
402 ** -K|--keep-merge-files On merge conflict, retain the temporary files
403 ** used for merging, named *-baseline, *-original,
404 ** and *-merge.
405 ** -n|--dry-run Do not actually change files on disk
406 ** --nosync Do not auto-sync prior to merging
407 ** -v|--verbose Show additional details of the merge
408 */
409 void merge_cmd(void){
410 int vid; /* Current version "V" */
@@ -800,15 +877,21 @@
877
878 /************************************************************************
879 ** All of the information needed to do the merge is now contained in the
880 ** FV table. Starting here, we begin to actually carry out the merge.
881 **
882 ** Begin by constructing the localdb.mergestat table.
883 */
884 merge_info_init();
885
886 /*
887 ** Find files that have changed from P->M but not P->V.
888 ** Copy the M content over into V.
889 */
890 db_prepare(&q,
891 /* 0 1 2 3 4 5 6 7 */
892 "SELECT idv, ridm, fn, islinkm, fnp, ridp, ridv, fnm FROM fv"
893 " WHERE idp>0 AND idv>0 AND idm>0"
894 " AND ridm!=ridp AND ridv=ridp AND NOT chnged"
895 );
896 while( db_step(&q)==SQLITE_ROW ){
897 int idv = db_column_int(&q, 0);
@@ -825,10 +908,21 @@
908 " THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
909 " WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
910 );
911 vfile_to_disk(0, idv, 0, 0);
912 }
913 db_multi_exec(
914 "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr)"
915 "VALUES('UPDATE',%Q,%d,%Q,%d,%Q,%d,%Q)",
916 /* fnp */ db_column_text(&q, 4),
917 /* ridp */ db_column_int(&q,5),
918 /* fn */ zName,
919 /* ridv */ db_column_int(&q,6),
920 /* fnm */ db_column_text(&q, 7),
921 /* ridm */ ridm,
922 /* fnr */ zName
923 );
924 }
925 db_finalize(&q);
926
927 /*
928 ** Do a three-way merge on files that have changes on both P->M and P->V.
@@ -836,11 +930,15 @@
930 ** Proceed even if the file doesn't exist on P, just like the common ancestor
931 ** of M and V is an empty file. In this case, merge conflict marks will be
932 ** added to the file and user will be forced to take a decision.
933 */
934 db_prepare(&q,
935 /* 0 1 2 3 4 5 6 7 8 */
936 "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm,"
937 /* 9 10 11 */
938 " fnp, fnm, chnged"
939 " FROM fv"
940 " WHERE idv>0 AND idm>0"
941 " AND ridm!=ridp AND (ridv!=ridp OR chnged)",
942 glob_expr("fv.fn", zBinGlob)
943 );
944 while( db_step(&q)==SQLITE_ROW ){
@@ -851,10 +949,11 @@
949 int isBinary = db_column_int(&q, 4);
950 const char *zName = db_column_text(&q, 5);
951 int isExe = db_column_int(&q, 6);
952 int islinkv = db_column_int(&q, 7);
953 int islinkm = db_column_int(&q, 8);
954 int chnged = db_column_int(&q, 11);
955 int rc;
956 char *zFullPath;
957 Blob m, p, r;
958 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
959 if( verboseFlag ){
@@ -864,13 +963,29 @@
963 fossil_print("MERGE %s\n", zName);
964 }
965 if( islinkv || islinkm ){
966 fossil_print("***** Cannot merge symlink %s\n", zName);
967 nConflict++;
968 db_multi_exec(
969 "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr,nc,msg)"
970 "VALUES('MERGE',%Q,%d,%Q,%d,%Q,%d,%Q,1,'cannot merge symlink')",
971 /* fnp */ db_column_text(&q, 9),
972 /* ridp */ ridp,
973 /* fn */ zName,
974 /* ridv */ ridv,
975 /* fnm */ db_column_text(&q, 10),
976 /* ridm */ ridm,
977 /* fnr */ zName
978 );
979 }else{
980 i64 sz;
981 const char *zErrMsg = 0;
982 int nc = 0;
983
984 if( !dryRunFlag ) undo_save(zName);
985 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
986 sz = file_size(zFullPath, ExtFILE);
987 content_get(ridp, &p);
988 content_get(ridm, &m);
989 if( isBinary ){
990 rc = -1;
991 blob_zero(&r);
@@ -887,15 +1002,35 @@
1002 db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
1003 if( rc>0 ){
1004 fossil_print("***** %d merge conflict%s in %s\n",
1005 rc, rc>1 ? "s" : "", zName);
1006 nConflict++;
1007 nc = rc;
1008 zErrMsg = "merge conflicts";
1009 }
1010 }else{
1011 fossil_print("***** Cannot merge binary file %s\n", zName);
1012 nConflict++;
1013 nc = 1;
1014 zErrMsg = "cannot merge binary file";
1015 }
1016 db_multi_exec(
1017 "INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
1018 "VALUES('MERGE',%Q,%d,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL),%Q,%d,"
1019 "%Q,%d,%Q)",
1020 /* fnp */ db_column_text(&q, 9),
1021 /* ridp */ ridp,
1022 /* fn */ zName,
1023 /* ridv */ chnged==0, ridv,
1024 /* sz */ chnged!=0, sz,
1025 /* fnm */ db_column_text(&q, 10),
1026 /* ridm */ ridm,
1027 /* fnr */ zName,
1028 /* nc */ nc,
1029 /* msg */ zErrMsg
1030 );
1031 fossil_free(zFullPath);
1032 blob_reset(&p);
1033 blob_reset(&m);
1034 blob_reset(&r);
1035 }
1036 vmerge_insert(idv, ridm);
@@ -904,22 +1039,33 @@
1039
1040 /*
1041 ** Drop files that are in P and V but not in M
1042 */
1043 db_prepare(&q,
1044 "SELECT idv, fn, chnged, ridv FROM fv"
1045 " WHERE idp>0 AND idv>0 AND idm=0"
1046 );
1047 while( db_step(&q)==SQLITE_ROW ){
1048 int idv = db_column_int(&q, 0);
1049 const char *zName = db_column_text(&q, 1);
1050 int chnged = db_column_int(&q, 2);
1051 int ridv = db_column_int(&q, 3);
1052 int sz = -1;
1053 const char *zErrMsg = 0;
1054 int nc = 0;
1055 /* Delete the file idv */
1056 fossil_print("DELETE %s\n", zName);
1057 if( chnged ){
1058 char *zFullPath;
1059 fossil_warning("WARNING: local edits lost for %s", zName);
1060 nConflict++;
1061 ridv = 0;
1062 nc = 1;
1063 zErrMsg = "local edits lost";
1064 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1065 sz = file_size(zFullPath, ExtFILE);
1066 fossil_free(zFullPath);
1067 }
1068 if( !dryRunFlag ) undo_save(zName);
1069 db_multi_exec(
1070 "UPDATE vfile SET deleted=1 WHERE id=%d", idv
1071 );
@@ -926,10 +1072,20 @@
1072 if( !dryRunFlag ){
1073 char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
1074 file_delete(zFullPath);
1075 free(zFullPath);
1076 }
1077 db_multi_exec(
1078 "INSERT INTO localdb.mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,nc,msg)"
1079 "VALUES('DELETE',NULL,NULL,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL),"
1080 "NULL,NULL,%d,%Q)",
1081 /* fn */ zName,
1082 /* ridv */ chnged==0, ridv,
1083 /* sz */ chnged!=0, sz,
1084 /* nc */ nc,
1085 /* msg */ zErrMsg
1086 );
1087 }
1088 db_finalize(&q);
1089
1090 /* For certain sets of renames (e.g. A -> B and B -> A), a file that is
1091 ** being renamed must first be moved to a temporary location to avoid
@@ -956,10 +1112,14 @@
1112 const char *zNewName = db_column_text(&q, 2);
1113 int isExe = db_column_int(&q, 3);
1114 fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
1115 if( !dryRunFlag ) undo_save(zOldName);
1116 if( !dryRunFlag ) undo_save(zNewName);
1117 db_multi_exec(
1118 "UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
1119 zOldName
1120 );
1121 db_multi_exec(
1122 "UPDATE vfile SET pathname=NULL, origname=pathname"
1123 " WHERE vid=%d AND pathname=%Q;"
1124 "UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
1125 " WHERE id=%d;",
@@ -1009,11 +1169,11 @@
1169
1170 /*
1171 ** Insert into V any files that are not in V or P but are in M.
1172 */
1173 db_prepare(&q,
1174 "SELECT idm, fnm, ridm FROM fv"
1175 " WHERE idp=0 AND idv=0 AND idm>0"
1176 );
1177 while( db_step(&q)==SQLITE_ROW ){
1178 int idm = db_column_int(&q, 0);
1179 const char *zName;
@@ -1042,10 +1202,17 @@
1202 nOverwrite++;
1203 }else{
1204 fossil_print("ADDED %s\n", zName);
1205 }
1206 fossil_free(zFullName);
1207 db_multi_exec(
1208 "INSERT INTO mergestat(op,fnm,ridm,fnr)"
1209 "VALUES('ADDED',%Q,%d,%Q)",
1210 /* fnm */ zName,
1211 /* ridm */ db_column_int(&q,2),
1212 /* fnr */ zName
1213 );
1214 if( !dryRunFlag ){
1215 undo_save(zName);
1216 vfile_to_disk(0, idm, 0, 0);
1217 }
1218 }
1219

Keyboard Shortcuts

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