Fossil SCM

fossil-scm / src / merge.c
Blame History Raw 1651 lines
1
/*
2
** Copyright (c) 2007 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains code used to merge two or more branches into
19
** a single tree.
20
*/
21
#include "config.h"
22
#include "merge.h"
23
#include <assert.h>
24
25
26
/*
27
** Bring up a Tcl/Tk GUI to show details of the most recent merge.
28
*/
29
static void merge_info_tk(int bDark, int bAll, int nContext){
30
int i;
31
Blob script;
32
const char *zTempFile = 0;
33
int bDebug;
34
char *zCmd;
35
const char *zTclsh;
36
zTclsh = find_option("tclsh",0,1);
37
if( zTclsh==0 ){
38
zTclsh = db_get("tclsh",0);
39
}
40
/* The undocumented --script FILENAME option causes the Tk script to
41
** be written into the FILENAME instead of being run. This is used
42
** for testing and debugging. */
43
zTempFile = find_option("script",0,1);
44
bDebug = find_option("tkdebug",0,0)!=0;
45
verify_all_options();
46
47
blob_zero(&script);
48
blob_appendf(&script, "set ncontext %d\n", nContext);
49
blob_appendf(&script, "set fossilexe {\"%/\"}\n", g.nameOfExe);
50
blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
51
g.nameOfExe);
52
blob_appendf(&script, "set filelist [list");
53
if( g.argc==2 ){
54
/* No files named on the command-line. Use every file mentioned
55
** in the MERGESTAT table to generate the file list. */
56
Stmt q;
57
int cnt = 0;
58
db_prepare(&q,
59
"WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
60
"('MERGE',1),('ADDED',2),('UPDATE',2))"
61
"SELECT coalesce(fnr,fn), op FROM mergestat JOIN priority USING(op)"
62
" %s ORDER BY pri, 1",
63
bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/
64
);
65
while( db_step(&q)==SQLITE_ROW ){
66
blob_appendf(&script," %s ", db_column_text(&q,1));
67
blob_append_tcl_literal(&script, db_column_text(&q,0),
68
db_column_bytes(&q,0));
69
cnt++;
70
}
71
db_finalize(&q);
72
if( cnt==0 ){
73
fossil_print(
74
"No interesting changes in this merge. Use --all to see everything\n"
75
);
76
return;
77
}
78
}else{
79
/* Use only files named on the command-line in the file list.
80
** But verify each file named is actually found in the MERGESTAT
81
** table first. */
82
for(i=2; i<g.argc; i++){
83
char *zFile; /* Input filename */
84
char *zTreename; /* Name of the file in the tree */
85
Blob fname; /* Filename relative to root */
86
char *zOp; /* Operation on this file */
87
zFile = mprintf("%/", g.argv[i]);
88
file_tree_name(zFile, &fname, 0, 1);
89
fossil_free(zFile);
90
zTreename = blob_str(&fname);
91
zOp = db_text(0, "SELECT op FROM mergestat WHERE fn=%Q or fnr=%Q",
92
zTreename, zTreename);
93
blob_appendf(&script, " %s ", zOp);
94
fossil_free(zOp);
95
blob_append_tcl_literal(&script, zTreename, (int)strlen(zTreename));
96
blob_reset(&fname);
97
}
98
}
99
blob_appendf(&script, "]\n");
100
blob_appendf(&script, "set darkmode %d\n", bDark!=0);
101
blob_appendf(&script, "set debug %d\n", bDebug!=0);
102
blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
103
if( zTempFile ){
104
blob_write_to_file(&script, zTempFile);
105
fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
106
}else{
107
#if defined(FOSSIL_ENABLE_TCL)
108
Th_FossilInit(TH_INIT_DEFAULT);
109
if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
110
blob_size(&script), 1, 1, 0)==TCL_OK ){
111
blob_reset(&script);
112
return;
113
}
114
/*
115
* If evaluation of the Tcl script fails, the reason may be that Tk
116
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
117
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
118
* to using the external "tclsh", if available.
119
*/
120
#endif
121
zTempFile = write_blob_to_temp_file(&script);
122
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
123
if( bDebug ){
124
fossil_print("%s\n", zCmd);
125
fflush(stdout);
126
}
127
fossil_system(zCmd);
128
file_delete(zTempFile);
129
fossil_free(zCmd);
130
}
131
blob_reset(&script);
132
}
133
134
/*
135
** Generate a TCL list on standard output that can be fed into the
136
** merge.tcl script to show the details of the most recent merge
137
** command associated with file "zFName". zFName must be the filename
138
** relative to the root of the check-in - in other words a "tree name".
139
**
140
** When this routine is called, we know that the mergestat table
141
** exists, but we do not know if zFName is mentioned in that table.
142
**
143
** The diffMode variable has these values:
144
**
145
** 0 Standard 3-way diff
146
** 12 2-way diff between baseline and local
147
** 13 2-way diff between baseline and merge-in
148
** 23 2-way diff between local and merge-in
149
*/
150
static void merge_info_tcl(const char *zFName, int nContext, int diffMode){
151
const char *zTreename;/* Name of the file in the tree */
152
Stmt q; /* To query the MERGESTAT table */
153
MergeBuilder mb; /* The merge builder object */
154
Blob pivot,v1,v2,out; /* Blobs for holding content */
155
const char *zFN; /* A filename */
156
int rid; /* RID value */
157
int sz; /* File size value */
158
159
zTreename = zFName;
160
db_prepare(&q,
161
/* 0 1 2 3 4 5 6 7 */
162
"SELECT fnp, ridp, fn, ridv, sz, fnm, ridm, fnr"
163
" FROM mergestat"
164
" WHERE fnp=%Q OR fnr=%Q",
165
zTreename, zTreename
166
);
167
if( db_step(&q)!=SQLITE_ROW ){
168
db_finalize(&q);
169
fossil_print("ERROR {don't know anything about file: %s}\n", zTreename);
170
return;
171
}
172
mergebuilder_init_tcl(&mb);
173
mb.nContext = nContext;
174
175
blob_zero(&pivot);
176
if( diffMode!=23 ){
177
/* Set up the pivot or baseline */
178
zFN = db_column_text(&q, 0);
179
if( zFN==0 ){
180
/* No pivot because the file was added */
181
mb.zPivot = "(no baseline)";
182
}else{
183
mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
184
rid = db_column_int(&q, 1);
185
content_get(rid, &pivot);
186
}
187
mb.pPivot = &pivot;
188
}
189
190
blob_zero(&v2);
191
if( diffMode!=12 ){
192
/* Set up the merge-in as V2 */
193
zFN = db_column_text(&q, 5);
194
if( zFN==0 ){
195
/* File deleted in the merged-in branch */
196
mb.zV2 = "(deleted file)";
197
}else{
198
mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
199
rid = db_column_int(&q, 6);
200
content_get(rid, &v2);
201
}
202
mb.pV2 = &v2;
203
}
204
205
blob_zero(&v1);
206
if( diffMode!=13 ){
207
/* Set up the local content as V1 */
208
zFN = db_column_text(&q, 2);
209
if( zFN==0 ){
210
/* File added by merge */
211
mb.zV1 = "(no original)";
212
}else{
213
mb.zV1 = mprintf("%s (local)", file_tail(zFN));
214
rid = db_column_int(&q, 3);
215
sz = db_column_int(&q, 4);
216
if( rid==0 && sz>0 ){
217
/* The origin file had been edited so we'll have to pull its
218
** original content out of the undo buffer */
219
Stmt q2;
220
db_prepare(&q2,
221
"SELECT content FROM undo"
222
" WHERE pathname=%Q AND octet_length(content)=%d",
223
zFN, sz
224
);
225
blob_zero(&v1);
226
if( db_step(&q2)==SQLITE_ROW ){
227
db_column_blob(&q2, 0, &v1);
228
}else{
229
mb.zV1 = "(local content missing)";
230
}
231
db_finalize(&q2);
232
}else{
233
/* The origin file was unchanged when the merge first occurred */
234
content_get(rid, &v1);
235
}
236
}
237
mb.pV1 = &v1;
238
}
239
240
blob_zero(&out);
241
if( diffMode==0 ){
242
/* Set up the output and do a 3-way diff */
243
zFN = db_column_text(&q, 7);
244
if( zFN==0 ){
245
mb.zOut = "(Merge Result)";
246
}else{
247
mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
248
}
249
mb.pOut = &out;
250
merge_three_blobs(&mb);
251
}else{
252
/* Set up to do a two-way diff */
253
Blob *pLeft, *pRight;
254
const char *zTagLeft, *zTagRight;
255
DiffConfig cfg;
256
memset(&cfg, 0, sizeof(cfg));
257
cfg.diffFlags = DIFF_TCL;
258
cfg.nContext = mb.nContext;
259
if( diffMode==12 || diffMode==13 ){
260
pLeft = &pivot;
261
zTagLeft = "baseline";
262
}else{
263
pLeft = &v1;
264
zTagLeft = "local";
265
}
266
if( diffMode==12 ){
267
pRight = &v1;
268
zTagRight = "local";
269
}else{
270
pRight = &v2;
271
zTagRight = "merge-in";
272
}
273
cfg.azLabel[0] = mprintf("%s (%s)", zFName, zTagLeft);
274
cfg.azLabel[1] = mprintf("%s (%s)", zFName, zTagRight);
275
diff_print_filenames("", "", &cfg, &out);
276
text_diff(pLeft, pRight, &out, &cfg);
277
fossil_free((char*)cfg.azLabel[0]);
278
fossil_free((char*)cfg.azLabel[1]);
279
}
280
281
blob_write_to_file(&out, "-");
282
mb.xDestroy(&mb);
283
blob_reset(&pivot);
284
blob_reset(&v1);
285
blob_reset(&v2);
286
blob_reset(&out);
287
db_finalize(&q);
288
}
289
290
/*
291
** COMMAND: merge-info
292
**
293
** Usage: %fossil merge-info [OPTIONS]
294
**
295
** Display information about the most recent merge operation.
296
**
297
** Options:
298
** -a|--all Show all file changes that happened because of
299
** the merge. Normally only MERGE, CONFLICT, and ERROR
300
** lines are shown
301
** -c|--context N Show N lines of context around each change,
302
** with negative N meaning show all content. Only
303
** meaningful in combination with --tcl or --tk.
304
** --dark Use dark mode for the Tcl/Tk-based GUI
305
** --tk Bring up a Tcl/Tk GUI that shows the changes
306
** associated with the most recent merge.
307
**
308
** Options used internally by --tk:
309
** --diff12 FILE Bring up a separate --tk diff for just the baseline
310
** and local variants of FILE.
311
** --diff13 FILE Like --diff12 but for baseline versus merge-in
312
** --diff23 FILE Like --diff12 but for local versus merge-in
313
** --tcl FILE Generate (to stdout) a TCL list containing
314
** information needed to display the changes to
315
** FILE caused by the most recent merge. FILE must
316
** be a pathname relative to the root of the check-out.
317
**
318
** Debugging options available only when --tk is used:
319
** --tkdebug Show sub-commands run to implement --tk
320
** --script FILE Write script used to implement --tk into FILE
321
*/
322
void merge_info_cmd(void){
323
const char *zCnt;
324
const char *zTcl;
325
int bTk;
326
int bDark;
327
int bAll;
328
int nContext;
329
Stmt q;
330
const char *zWhere;
331
int cnt = 0;
332
const char *zDiff2 = 0;
333
int diffMode = 0;
334
335
db_must_be_within_tree();
336
bTk = find_option("tk", 0, 0)!=0;
337
zTcl = find_option("tcl", 0, 1);
338
zCnt = find_option("context", "c", 1);
339
bDark = find_option("dark", 0, 0)!=0;
340
bAll = find_option("all", "a", 0)!=0;
341
if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){
342
diffMode = 12;
343
}else
344
if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){
345
diffMode = 13;
346
}else
347
if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){
348
diffMode = 23;
349
}
350
351
if( zCnt ){
352
nContext = atoi(zCnt);
353
if( nContext<0 ) nContext = 0xfffffff;
354
}else{
355
nContext = 6;
356
}
357
if( !db_table_exists("localdb","mergestat") ){
358
if( zTcl ){
359
fossil_print("ERROR {no merge data available}\n");
360
}else{
361
fossil_print("No merge data is available\n");
362
}
363
return;
364
}
365
if( bTk ){
366
merge_info_tk(bDark, bAll, nContext);
367
return;
368
}
369
if( zTcl ){
370
if( diffMode ) zTcl = zDiff2;
371
merge_info_tcl(zTcl, nContext, diffMode);
372
return;
373
}
374
if( diffMode ){
375
char *zCmd;
376
zCmd = mprintf("merge-info --diff%d %!$ -c %d%s",
377
diffMode, zDiff2, nContext, bDark ? " --dark" : "");
378
diff_tk(zCmd, g.argc);
379
fossil_free(zCmd);
380
return;
381
}
382
383
verify_all_options();
384
if( g.argc>2 ){
385
usage("[OPTIONS]");
386
}
387
388
if( bAll ){
389
zWhere = "";
390
}else{
391
zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
392
}
393
db_prepare(&q,
394
"WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
395
"('MERGE',1),('ADDED',2),('UPDATE',2))"
396
397
/* 0 1 2 */
398
"SELECT op, coalesce(fnr,fn), msg"
399
" FROM mergestat JOIN priority USING(op)"
400
" %s"
401
" ORDER BY pri, coalesce(fnr,fn)",
402
zWhere /*safe-for-%s*/
403
);
404
while( db_step(&q)==SQLITE_ROW ){
405
const char *zOp = db_column_text(&q, 0);
406
const char *zName = db_column_text(&q, 1);
407
const char *zErr = db_column_text(&q, 2);
408
if( zErr && fossil_strcmp(zOp,"CONFLICT")!=0 ){
409
fossil_print("%-9s %s (%s)\n", zOp, zName, zErr);
410
}else{
411
fossil_print("%-9s %s\n", zOp, zName);
412
}
413
cnt++;
414
}
415
db_finalize(&q);
416
if( !bAll && cnt==0 ){
417
fossil_print(
418
"No interesting changes in this merge. Use --all to see everything.\n"
419
);
420
}
421
}
422
423
/*
424
** Erase all information about prior merges. Do this, for example, after
425
** a commit.
426
*/
427
void merge_info_forget(void){
428
db_multi_exec(
429
"DROP TABLE IF EXISTS localdb.mergestat;"
430
"DELETE FROM localdb.vvar WHERE name glob 'mergestat-*';"
431
);
432
}
433
434
435
/*
436
** Initialize the MERGESTAT table.
437
**
438
** Notes about mergestat:
439
**
440
** * ridv is a positive integer and sz is NULL if the V file contained
441
** no local edits prior to the merge. If the V file was modified prior
442
** to the merge then ridv is NULL and sz is the size of the file prior
443
** to merge.
444
**
445
** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
446
** added by merge.
447
*/
448
void merge_info_init(void){
449
merge_info_forget();
450
db_multi_exec(
451
"CREATE TABLE localdb.mergestat(\n"
452
" op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
453
" fnp TEXT, -- Name of the pivot file (P)\n"
454
" ridp INT, -- RID for the pivot file\n"
455
" fn TEXT, -- Name of origin file (V)\n"
456
" ridv INT, -- RID for origin file, or NULL if previously edited\n"
457
" sz INT, -- Size of origin file in bytes, NULL if unedited\n"
458
" fnm TEXT, -- Name of the file being merged in (M)\n"
459
" ridm INT, -- RID for the merge-in file\n"
460
" fnr TEXT, -- Name of the final output file, after all renaming\n"
461
" nc INT DEFAULT 0, -- Number of conflicts\n"
462
" msg TEXT -- Error message\n"
463
");"
464
);
465
}
466
467
/*
468
** Print information about a particular check-in.
469
*/
470
void print_checkin_description(int rid, int indent, const char *zLabel){
471
Stmt q;
472
db_prepare(&q,
473
"SELECT datetime(mtime,toLocal()),"
474
" coalesce(euser,user), coalesce(ecomment,comment),"
475
" (SELECT uuid FROM blob WHERE rid=%d),"
476
" (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref"
477
" WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid"
478
" AND tagxref.rid=%d AND tagxref.tagtype>0)"
479
" FROM event WHERE objid=%d", rid, rid, rid);
480
if( db_step(&q)==SQLITE_ROW ){
481
const char *zTagList = db_column_text(&q, 4);
482
char *zCom;
483
if( zTagList && zTagList[0] ){
484
zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList);
485
}else{
486
zCom = fossil_strdup(db_column_text(&q,2));
487
}
488
fossil_print("%-*s [%S] by %s on %s\n%*s",
489
indent-1, zLabel,
490
db_column_text(&q, 3),
491
db_column_text(&q, 1),
492
db_column_text(&q, 0),
493
indent, "");
494
comment_print(zCom, db_column_text(&q,2), indent, -1, get_comment_format());
495
fossil_free(zCom);
496
}
497
db_finalize(&q);
498
}
499
500
501
/* Pick the most recent leaf that is (1) not equal to vid and (2)
502
** has not already been merged into vid and (3) the leaf is not
503
** closed and (4) the leaf is in the same branch as vid.
504
**
505
** Set vmergeFlag to control whether the vmerge table is checked.
506
*/
507
int fossil_find_nearest_fork(int vid, int vmergeFlag){
508
Blob sql;
509
Stmt q;
510
int rid = 0;
511
512
blob_zero(&sql);
513
blob_append_sql(&sql,
514
"SELECT leaf.rid"
515
" FROM leaf, event"
516
" WHERE leaf.rid=event.objid"
517
" AND leaf.rid!=%d", /* Constraint (1) */
518
vid
519
);
520
if( vmergeFlag ){
521
blob_append_sql(&sql,
522
" AND leaf.rid NOT IN (SELECT merge FROM vmerge)" /* Constraint (2) */
523
);
524
}
525
blob_append_sql(&sql,
526
" AND NOT EXISTS(SELECT 1 FROM tagxref" /* Constraint (3) */
527
" WHERE rid=leaf.rid"
528
" AND tagid=%d"
529
" AND tagtype>0)"
530
" AND (SELECT value FROM tagxref" /* Constraint (4) */
531
" WHERE tagid=%d AND rid=%d AND tagtype>0) ="
532
" (SELECT value FROM tagxref"
533
" WHERE tagid=%d AND rid=leaf.rid AND tagtype>0)"
534
" ORDER BY event.mtime DESC LIMIT 1",
535
TAG_CLOSED, TAG_BRANCH, vid, TAG_BRANCH
536
);
537
db_prepare(&q, "%s", blob_sql_text(&sql));
538
blob_reset(&sql);
539
if( db_step(&q)==SQLITE_ROW ){
540
rid = db_column_int(&q, 0);
541
}
542
db_finalize(&q);
543
return rid;
544
}
545
546
/*
547
** Check content that was received with rcvid and return true if any
548
** fork was created.
549
*/
550
int fossil_any_has_fork(int rcvid){
551
static Stmt q;
552
int fForkSeen = 0;
553
554
if( rcvid==0 ) return 0;
555
db_static_prepare(&q,
556
" SELECT pid FROM plink WHERE pid>0 AND isprim"
557
" AND cid IN (SELECT blob.rid FROM blob"
558
" WHERE rcvid=:rcvid)");
559
db_bind_int(&q, ":rcvid", rcvid);
560
while( !fForkSeen && db_step(&q)==SQLITE_ROW ){
561
int pid = db_column_int(&q, 0);
562
if( count_nonbranch_children(pid)>1 ){
563
compute_leaves(pid,1);
564
if( db_int(0, "SELECT count(*) FROM leaves")>1 ){
565
int rid = db_int(0, "SELECT rid FROM leaves, event"
566
" WHERE event.objid=leaves.rid"
567
" ORDER BY event.mtime DESC LIMIT 1");
568
fForkSeen = fossil_find_nearest_fork(rid, db_open_local(0))!=0;
569
}
570
}
571
}
572
db_finalize(&q);
573
return fForkSeen;
574
}
575
576
/*
577
** Add an entry to the FV table for all files renamed between
578
** version N and the version specified by vid.
579
*/
580
static void add_renames(
581
const char *zFnCol, /* The FV column for the filename in vid */
582
int vid, /* The desired version's- RID */
583
int nid, /* The check-in rid for the name pivot */
584
int revOK, /* OK to move backwards (child->parent) if true */
585
const char *zDebug /* Generate trace output if not NULL */
586
){
587
int nChng; /* Number of file name changes */
588
int *aChng; /* An array of file name changes */
589
int i; /* Loop counter */
590
find_filename_changes(nid, vid, revOK, &nChng, &aChng, zDebug);
591
if( nChng==0 ) return;
592
for(i=0; i<nChng; i++){
593
char *zN, *zV;
594
zN = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2]);
595
zV = db_text(0, "SELECT name FROM filename WHERE fnid=%d", aChng[i*2+1]);
596
db_multi_exec(
597
"INSERT OR IGNORE INTO fv(%s,fnn) VALUES(%Q,%Q)",
598
zFnCol /*safe-for-%s*/, zV, zN
599
);
600
if( db_changes()==0 ){
601
db_multi_exec(
602
"UPDATE fv SET %s=%Q WHERE fnn=%Q",
603
zFnCol /*safe-for-%s*/, zV, zN
604
);
605
}
606
free(zN);
607
free(zV);
608
}
609
free(aChng);
610
}
611
612
/* Make an entry in the vmerge table for the given id, and rid.
613
*/
614
static void vmerge_insert(int id, int rid){
615
db_multi_exec(
616
"INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
617
"VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d))",
618
id, rid, rid
619
);
620
}
621
622
/*
623
** Print the contents of the "fv" table on standard output, for debugging
624
** purposes.
625
**
626
** Only show entries where a file has changed, unless showAll is true.
627
*/
628
static void debug_fv_dump(int showAll){
629
Stmt q;
630
if( showAll ){
631
db_prepare(&q,
632
"SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
633
" isexe, islinkv, islinkm, fnn FROM fv"
634
);
635
}else{
636
db_prepare(&q,
637
"SELECT rowid, fn, fnp, fnm, chnged, ridv, ridp, ridm, "
638
" isexe, islinkv, islinkm, fnn FROM fv"
639
" WHERE chnged OR (ridv!=ridm AND ridm!=ridp)"
640
);
641
}
642
while( db_step(&q)==SQLITE_ROW ){
643
fossil_print("%3d: ridv=%-4d ridp=%-4d ridm=%-4d chnged=%d isexe=%d "
644
" islinkv=%d islinkm=%d\n",
645
db_column_int(&q, 0),
646
db_column_int(&q, 5),
647
db_column_int(&q, 6),
648
db_column_int(&q, 7),
649
db_column_int(&q, 4),
650
db_column_int(&q, 8),
651
db_column_int(&q, 9),
652
db_column_int(&q, 10));
653
fossil_print(" fn = [%s]\n", db_column_text(&q, 1));
654
fossil_print(" fnp = [%s]\n", db_column_text(&q, 2));
655
fossil_print(" fnm = [%s]\n", db_column_text(&q, 3));
656
fossil_print(" fnn = [%s]\n", db_column_text(&q, 11));
657
}
658
db_finalize(&q);
659
}
660
661
/*
662
** Print the content of the VFILE table on standard output, for
663
** debugging purposes.
664
*/
665
static void debug_show_vfile(void){
666
Stmt q;
667
int pvid = -1;
668
db_prepare(&q,
669
"SELECT vid, id, chnged, deleted, isexe, islink, rid, mrid, mtime,"
670
" pathname, origname, mhash FROM vfile"
671
" ORDER BY vid, pathname"
672
);
673
while( db_step(&q)==SQLITE_ROW ){
674
int vid = db_column_int(&q, 0);
675
int chnged = db_column_int(&q, 2);
676
int dltd = db_column_int(&q, 3);
677
int isexe = db_column_int(&q, 4);
678
int islnk = db_column_int(&q, 5);
679
int rid = db_column_int(&q, 6);
680
int mrid = db_column_int(&q, 7);
681
const char *zPath = db_column_text(&q, 9);
682
const char *zOrig = db_column_text(&q, 10);
683
if( vid!=pvid ){
684
fossil_print("VFILE vid=%d (%z):\n", vid,
685
db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid));
686
pvid = vid;
687
}
688
fossil_print(" rid %-6d mrid %-6d %4s %3s %3s %3s %s",
689
rid, mrid,
690
chnged ? "chng" : "",
691
dltd ? "del" : "",
692
isexe ? "exe" : "",
693
islnk ? "lnk" : "", zPath);
694
if( zOrig && zOrig[0] ){
695
fossil_print(" <- %s\n", zOrig);
696
}else{
697
fossil_print("\n");
698
}
699
}
700
db_finalize(&q);
701
}
702
703
/*
704
** COMMAND: test-show-vfile
705
** Usage: %fossil test-show-vfile
706
**
707
** Show the content of the VFILE table in a local check-out.
708
*/
709
void test_show_vfile_cmd(void){
710
if( g.argc!=2 ){
711
fossil_fatal("unknown arguments to the %s command\n", g.argv[1]);
712
}
713
verify_all_options();
714
db_must_be_within_tree();
715
debug_show_vfile();
716
}
717
718
719
/*
720
** COMMAND: merge
721
** COMMAND: cherry-pick alias
722
** COMMAND: cherrypick
723
**
724
** Usage: %fossil merge ?OPTIONS? ?VERSION ...?
725
** Or: %fossil cherrypick ?OPTIONS? ?VERSION ...?
726
**
727
** The argument VERSION is a version that should be merged into the
728
** current check-out. All changes from VERSION back to the nearest
729
** common ancestor are merged. Except, if either of the --cherrypick
730
** or --backout options are used only the changes associated with the
731
** single check-in VERSION are merged. The --backout option causes
732
** the changes associated with VERSION to be removed from the current
733
** check-out rather than added. When invoked with the name
734
** "cherrypick" instead of "merge", this command works exactly like
735
** "merge --cherrypick".
736
**
737
** Files which are renamed in the merged-in branch will be renamed in
738
** the current check-out.
739
**
740
** If the VERSION argument is omitted, then Fossil attempts to find
741
** a recent fork on the current branch to merge.
742
**
743
** Note that this command does not commit the merge, as that is a
744
** separate step.
745
**
746
** If there are multiple VERSION arguments, then each VERSION is merged
747
** (or cherrypicked) in the order that they appear on the command-line.
748
**
749
** Options:
750
** --backout Do a reverse cherrypick merge against VERSION.
751
** In other words, back out the changes that were
752
** added by VERSION.
753
** --baseline BASELINE Use BASELINE as the "pivot" of the merge instead
754
** of the nearest common ancestor. This allows
755
** a sequence of changes in a branch to be merged
756
** without having to merge the entire branch.
757
** --binary GLOBPATTERN Treat files that match GLOBPATTERN as binary
758
** and do not try to merge parallel changes. This
759
** option overrides the "binary-glob" setting.
760
** --cherrypick Do a cherrypick merge VERSION into the current
761
** check-out. A cherrypick merge pulls in the changes
762
** of the single check-in VERSION, rather than all
763
** changes back to the nearest common ancestor.
764
** -f|--force Force the merge even if it would be a no-op
765
** --force-missing Force the merge even if there is missing content
766
** --integrate Merged branch will be closed when committing
767
** -K|--keep-merge-files On merge conflict, retain the temporary files
768
** used for merging, named *-baseline, *-original,
769
** and *-merge.
770
** -n|--dry-run Do not actually change files on disk
771
** --nosync Do not auto-sync prior to merging
772
** --noundo Do not record changes in the undo log
773
** -v|--verbose Show additional details of the merge
774
*/
775
void merge_cmd(void){
776
int vid; /* Current version "V" */
777
int mid; /* Version we are merging from "M" */
778
int pid = 0; /* The pivot version - most recent common ancestor P */
779
int nid = 0; /* The name pivot version "N" */
780
int verboseFlag; /* True if the -v|--verbose option is present */
781
int integrateFlag; /* True if the --integrate option is present */
782
int pickFlag; /* True if the --cherrypick option is present */
783
int backoutFlag; /* True if the --backout option is present */
784
int dryRunFlag; /* True if the --dry-run or -n option is present */
785
int forceFlag; /* True if the --force or -f option is present */
786
int forceMissingFlag; /* True if the --force-missing option is present */
787
const char *zBinGlob; /* The value of --binary */
788
const char *zPivot; /* The value of --baseline */
789
int debugFlag; /* True if --debug is present */
790
int showVfileFlag; /* True if the --show-vfile flag is present */
791
int keepMergeFlag; /* True if --keep-merge-files is present */
792
int nConflict = 0; /* Number of conflicts seen */
793
int nOverwrite = 0; /* Number of unmanaged files overwritten */
794
char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
795
const char *zVersion; /* The VERSION argument */
796
int bMultiMerge = 0; /* True if there are two or more VERSION arguments */
797
int nMerge = 0; /* Number of prior merges processed */
798
int useUndo = 1; /* True to record changes in the undo log */
799
Stmt q; /* SQL statement used for merge processing */
800
801
802
/* Notation:
803
**
804
** V The current check-out
805
** M The version being merged in
806
** P The "pivot" - the most recent common ancestor of V and M.
807
** N The "name pivot" - for detecting renames
808
*/
809
810
undo_capture_command_line();
811
verboseFlag = find_option("verbose","v",0)!=0;
812
forceMissingFlag = find_option("force-missing",0,0)!=0;
813
if( !verboseFlag ){
814
verboseFlag = find_option("detail",0,0)!=0; /* deprecated */
815
}
816
pickFlag = find_option("cherrypick",0,0)!=0;
817
if('c'==*g.zCmdName /*called as cherrypick, possibly a short form*/){
818
pickFlag = 1;
819
}
820
integrateFlag = find_option("integrate",0,0)!=0;
821
backoutFlag = find_option("backout",0,0)!=0;
822
zBinGlob = find_option("binary",0,1);
823
dryRunFlag = find_option("dry-run","n",0)!=0;
824
if( !dryRunFlag ){
825
dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */
826
}
827
if( find_option("nosync",0,0) ) g.fNoSync = 1;
828
forceFlag = find_option("force","f",0)!=0;
829
zPivot = find_option("baseline",0,1);
830
keepMergeFlag = find_option("keep-merge-files", "K",0)!=0;
831
832
/* Undocumented --debug and --show-vfile options:
833
**
834
** When included on the command-line, --debug causes lots of state
835
** information to be displayed. This option is undocumented as it
836
** might change or be eliminated in future releases.
837
**
838
** The --show-vfile flag does a dump of the VFILE table for reference.
839
**
840
** Hints:
841
** * Combine --debug and --verbose for still more output.
842
** * The --dry-run option is also useful in combination with --debug.
843
*/
844
debugFlag = find_option("debug",0,0)!=0;
845
if( debugFlag && verboseFlag ) debugFlag = 2;
846
showVfileFlag = find_option("show-vfile",0,0)!=0;
847
useUndo = find_option("noundo",0,0)==0;
848
if( dryRunFlag ) useUndo = 0;
849
850
verify_all_options();
851
db_must_be_within_tree();
852
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
853
vid = db_lget_int("checkout", 0);
854
if( vid==0 ){
855
fossil_fatal("nothing is checked out");
856
}
857
if( forceFlag==0 && leaf_is_closed(vid) ){
858
fossil_fatal("cannot merge into a closed leaf. Use --force to override");
859
}
860
if( !dryRunFlag ){
861
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "merge") ){
862
fossil_fatal("merge abandoned due to sync failure");
863
}
864
}
865
866
/*
867
** A "multi-merge" means two or more other check-ins are being merged
868
** into the current check-in. In other words, there are two or more
869
** VERSION arguments on the command-line. Multi-merge works by doing
870
** the merges one by one, as long as there are no conflicts. At the
871
** bottom of this routine, a jump is made back up to this point if there
872
** are more merges yet to be done and no errors have yet been seen.
873
**
874
** Related variables:
875
** bMultiMerge True if there are one or more merges yet to do
876
** zVersion The name of the current checking being merged in
877
** nMerge Number of prior merges
878
*/
879
merge_next_child:
880
881
/* Find mid, the artifactID of the version to be merged into
882
** the current check-out.
883
*/
884
if( g.argc>=3 ){
885
int i;
886
/* Mid is specified as an argument on the command-line */
887
zVersion = g.argv[2];
888
mid = name_to_typed_rid(zVersion, "ci");
889
if( mid==0 || !is_a_version(mid) ){
890
fossil_fatal("not a version: %s", zVersion);
891
}
892
bMultiMerge = g.argc>3;
893
if( bMultiMerge ){
894
for(i=3; i<g.argc; i++) g.argv[i-1] = g.argv[i];
895
g.argc--;
896
}
897
}else if( g.argc==2 ){
898
/* No version specified on the command-line so pick the most recent
899
** leaf that is (1) not the version currently checked out and (2)
900
** has not already been merged into the current check-out and (3)
901
** the leaf is not closed and (4) the leaf is in the same branch
902
** as the current check-out.
903
*/
904
Stmt q;
905
if( pickFlag || backoutFlag || integrateFlag){
906
fossil_fatal("cannot use --backout, --cherrypick or --integrate "
907
"with a fork merge");
908
}
909
mid = fossil_find_nearest_fork(vid, db_open_local(0));
910
if( mid==0 ){
911
fossil_fatal("no unmerged forks of branch \"%s\"",
912
db_text(0, "SELECT value FROM tagxref"
913
" WHERE tagid=%d AND rid=%d AND tagtype>0",
914
TAG_BRANCH, vid)
915
);
916
}
917
db_prepare(&q,
918
"SELECT blob.uuid,"
919
" datetime(event.mtime,toLocal()),"
920
" coalesce(ecomment, comment),"
921
" coalesce(euser, user)"
922
" FROM event, blob"
923
" WHERE event.objid=%d AND blob.rid=%d",
924
mid, mid
925
);
926
zVersion = 0;
927
if( db_step(&q)==SQLITE_ROW ){
928
char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
929
db_column_text(&q, 0), db_column_text(&q, 1),
930
db_column_text(&q, 3), db_column_text(&q, 2));
931
comment_print(zCom, db_column_text(&q,2), 0, -1, get_comment_format());
932
fossil_free(zCom);
933
zVersion = mprintf("%S",db_column_text(&q,0));
934
}
935
db_finalize(&q);
936
}else{
937
usage("?OPTIONS? ?VERSION?");
938
return;
939
}
940
941
if( zPivot ){
942
pid = name_to_typed_rid(zPivot, "ci");
943
if( pid==0 || !is_a_version(pid) ){
944
fossil_fatal("not a version: %s", zPivot);
945
}
946
if( pickFlag ){
947
fossil_fatal("incompatible options: --cherrypick and --baseline");
948
}
949
}
950
if( pickFlag || backoutFlag ){
951
if( integrateFlag ){
952
fossil_fatal("incompatible options: --integrate and --cherrypick "
953
"with --backout");
954
}
955
pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
956
if( pid<=0 ){
957
fossil_fatal("cannot find an ancestor for %s", zVersion);
958
}
959
}else{
960
if( !zPivot ){
961
pivot_set_primary(mid);
962
pivot_set_secondary(vid);
963
db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
964
while( db_step(&q)==SQLITE_ROW ){
965
pivot_set_secondary(db_column_int(&q,0));
966
}
967
db_finalize(&q);
968
pid = pivot_find(0);
969
if( pid<=0 ){
970
fossil_fatal("cannot find a common ancestor between the current "
971
"check-out and %s", zVersion);
972
}
973
}
974
pivot_set_primary(mid);
975
pivot_set_secondary(vid);
976
nid = pivot_find(1);
977
if( nid!=pid ){
978
pivot_set_primary(nid);
979
pivot_set_secondary(pid);
980
nid = pivot_find(1);
981
}
982
}
983
if( backoutFlag ){
984
int t = pid;
985
pid = mid;
986
mid = t;
987
}
988
if( nid==0 ) nid = pid;
989
if( !is_a_version(pid) ){
990
fossil_fatal("not a version: record #%d", pid);
991
}
992
if( !forceFlag && (mid==pid || mid==vid) ){
993
fossil_print("Merge skipped because it is a no-op. "
994
" Use --force to override.\n");
995
return;
996
}
997
if( integrateFlag && !is_a_leaf(mid)){
998
fossil_warning("ignoring --integrate: %s is not a leaf", zVersion);
999
integrateFlag = 0;
1000
}
1001
if( integrateFlag && content_is_private(mid) ){
1002
fossil_warning(
1003
"ignoring --integrate: %s is on a private branch"
1004
"\n Use \"fossil amend --close\" (after commit) to close the leaf.",
1005
zVersion);
1006
integrateFlag = 0;
1007
}
1008
if( verboseFlag ){
1009
print_checkin_description(mid, 12,
1010
integrateFlag ? "integrate:" : "merge-from:");
1011
print_checkin_description(pid, 12, "baseline:");
1012
}
1013
vfile_check_signature(vid, CKSIG_ENOTFILE);
1014
if( nMerge==0 ) db_begin_transaction();
1015
if( useUndo ) undo_begin();
1016
if( load_vfile_from_rid(mid) && !forceMissingFlag ){
1017
fossil_fatal("missing content, unable to merge");
1018
}
1019
if( load_vfile_from_rid(pid) && !forceMissingFlag ){
1020
fossil_fatal("missing content, unable to merge");
1021
}
1022
if( zPivot ){
1023
vAncestor = db_exists(
1024
"WITH RECURSIVE ancestor(id) AS ("
1025
" VALUES(%d)"
1026
" UNION"
1027
" SELECT pid FROM plink, ancestor"
1028
" WHERE cid=ancestor.id AND pid!=%d AND cid!=%d)"
1029
"SELECT 1 FROM ancestor WHERE id=%d LIMIT 1",
1030
vid, nid, pid, pid
1031
) ? 'p' : 'n';
1032
}
1033
if( debugFlag ){
1034
char *z;
1035
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nid);
1036
fossil_print("N=%-4d %z (file rename pivot)\n", nid, z);
1037
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid);
1038
fossil_print("P=%-4d %z (file content pivot)\n", pid, z);
1039
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid);
1040
fossil_print("M=%-4d %z (merged-in version)\n", mid, z);
1041
z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
1042
fossil_print("V=%-4d %z (current version)\n", vid, z);
1043
fossil_print("vAncestor = '%c'\n", vAncestor);
1044
}
1045
if( showVfileFlag ) debug_show_vfile();
1046
1047
/*
1048
** The vfile.pathname field is used to match files against each other. The
1049
** FV table contains one row for each unique filename in
1050
** in the current check-out, the pivot, and the version being merged.
1051
*/
1052
db_multi_exec(
1053
"DROP TABLE IF EXISTS fv;"
1054
"CREATE TEMP TABLE fv(\n"
1055
" fn TEXT UNIQUE %s,\n" /* The filename */
1056
" idv INTEGER DEFAULT 0,\n" /* VFILE entry for current version */
1057
" idp INTEGER DEFAULT 0,\n" /* VFILE entry for the pivot */
1058
" idm INTEGER DEFAULT 0,\n" /* VFILE entry for version merging in */
1059
" chnged BOOLEAN,\n" /* True if current version has been edited */
1060
" ridv INTEGER DEFAULT 0,\n" /* Record ID for current version */
1061
" ridp INTEGER DEFAULT 0,\n" /* Record ID for pivot */
1062
" ridm INTEGER DEFAULT 0,\n" /* Record ID for merge */
1063
" isexe BOOLEAN,\n" /* Execute permission enabled */
1064
" fnp TEXT UNIQUE %s,\n" /* The filename in the pivot */
1065
" fnm TEXT UNIQUE %s,\n" /* The filename in the merged version */
1066
" fnn TEXT UNIQUE %s,\n" /* The filename in the name pivot */
1067
" islinkv BOOLEAN,\n" /* True if current version is a symlink */
1068
" islinkm BOOLEAN\n" /* True if merged version in is a symlink */
1069
");",
1070
filename_collation(), filename_collation(), filename_collation(),
1071
filename_collation()
1072
);
1073
1074
/*
1075
** Compute name changes from N to V, P, and M
1076
*/
1077
add_renames("fn", vid, nid, 0, debugFlag ? "N->V" : 0);
1078
add_renames("fnp", pid, nid, 0, debugFlag ? "N->P" : 0);
1079
add_renames("fnm", mid, nid, backoutFlag, debugFlag ? "N->M" : 0);
1080
if( debugFlag ){
1081
fossil_print("******** FV after name change search *******\n");
1082
debug_fv_dump(1);
1083
}
1084
if( nid!=pid ){
1085
/* See forum thread https://fossil-scm.org/forum/forumpost/549700437b
1086
**
1087
** If a filename changes between nid and one of the other check-ins
1088
** pid, vid, or mid, then it might not have changed for all of them.
1089
** try to fill in the appropriate filename in all slots where the
1090
** name is missing.
1091
**
1092
** This does not work if
1093
** (1) The filename changes more than once in between nid and vid/mid
1094
** (2) Two or more filenames swap places - for example if A is renamed
1095
** to B and B is renamed to A.
1096
** The Fossil merge algorithm breaks down in those cases. It will need
1097
** to be completely rewritten to handle such complex cases. Such cases
1098
** appear to be rare, and also confusing to humans.
1099
*/
1100
db_multi_exec(
1101
"UPDATE OR IGNORE fv SET fnp=vfile.pathname FROM vfile"
1102
" WHERE fnp IS NULL"
1103
" AND vfile.pathname = fv.fnn"
1104
" AND vfile.vid=%d;",
1105
pid
1106
);
1107
db_multi_exec(
1108
"UPDATE OR IGNORE fv SET fn=vfile.pathname FROM vfile"
1109
" WHERE fn IS NULL"
1110
" AND vfile.pathname = coalesce(fv.fnp,fv.fnn)"
1111
" AND vfile.vid=%d;",
1112
vid
1113
);
1114
db_multi_exec(
1115
"UPDATE OR IGNORE fv SET fnm=vfile.pathname FROM vfile"
1116
" WHERE fnm IS NULL"
1117
" AND vfile.pathname = coalesce(fv.fnp,fv.fnn)"
1118
" AND vfile.vid=%d;",
1119
mid
1120
);
1121
db_multi_exec(
1122
"UPDATE OR IGNORE fv SET fnp=vfile.pathname FROM vfile"
1123
" WHERE fnp IS NULL"
1124
" AND vfile.pathname IN (fv.fnm,fv.fn)"
1125
" AND vfile.vid=%d;",
1126
pid
1127
);
1128
db_multi_exec(
1129
"UPDATE OR IGNORE fv SET fn=vfile.pathname FROM vfile"
1130
" WHERE fn IS NULL"
1131
" AND vfile.pathname = fv.fnm"
1132
" AND vfile.vid=%d;",
1133
vid
1134
);
1135
db_multi_exec(
1136
"UPDATE OR IGNORE fv SET fnm=vfile.pathname FROM vfile"
1137
" WHERE fnm IS NULL"
1138
" AND vfile.pathname = fv.fn"
1139
" AND vfile.vid=%d;",
1140
mid
1141
);
1142
}
1143
if( debugFlag ){
1144
fossil_print("******** FV after name change fill-in *******\n");
1145
debug_fv_dump(1);
1146
}
1147
1148
/*
1149
** Add files found in V
1150
*/
1151
db_multi_exec(
1152
"UPDATE OR IGNORE fv SET fn=coalesce(fn%c,fnn) WHERE fn IS NULL;"
1153
"REPLACE INTO fv(fn,fnp,fnm,fnn,idv,ridv,islinkv,isexe,chnged)"
1154
" SELECT pathname, fnp, fnm, fnn, id, rid, islink, vf.isexe, vf.chnged"
1155
" FROM vfile vf"
1156
" LEFT JOIN fv ON fn=coalesce(origname,pathname)"
1157
" AND rid>0 AND vf.chnged NOT IN (3,5)"
1158
" WHERE vid=%d;",
1159
vAncestor, vid
1160
);
1161
if( debugFlag>=2 ){
1162
fossil_print("******** FV after adding files in current version *******\n");
1163
debug_fv_dump(1);
1164
}
1165
1166
/*
1167
** Add files found in P
1168
*/
1169
db_multi_exec(
1170
"UPDATE OR IGNORE fv SET fnp=coalesce(fnn,"
1171
" (SELECT coalesce(origname,pathname) FROM vfile WHERE id=idv))"
1172
" WHERE fnp IS NULL;"
1173
"INSERT OR IGNORE INTO fv(fnp)"
1174
" SELECT coalesce(origname,pathname) FROM vfile WHERE vid=%d;",
1175
pid
1176
);
1177
if( debugFlag>=2 ){
1178
fossil_print("******** FV after adding pivot files *******\n");
1179
debug_fv_dump(1);
1180
}
1181
1182
/*
1183
** Add files found in M
1184
*/
1185
db_multi_exec(
1186
"UPDATE OR IGNORE fv SET fnm=fnp WHERE fnm IS NULL;"
1187
"INSERT OR IGNORE INTO fv(fnm)"
1188
" SELECT pathname FROM vfile WHERE vid=%d;",
1189
mid
1190
);
1191
if( debugFlag>=2 ){
1192
fossil_print("******** FV after adding merge-in files *******\n");
1193
debug_fv_dump(1);
1194
}
1195
1196
/*
1197
** Compute the file version ids for P and M
1198
*/
1199
if( pid==vid ){
1200
db_multi_exec(
1201
"UPDATE fv SET idp=idv, ridp=ridv WHERE ridv>0 AND chnged NOT IN (3,5)"
1202
);
1203
}else{
1204
db_multi_exec(
1205
"UPDATE fv SET idp=coalesce(vfile.id,0), ridp=coalesce(vfile.rid,0)"
1206
" FROM vfile"
1207
" WHERE vfile.vid=%d AND fv.fnp=vfile.pathname",
1208
pid
1209
);
1210
}
1211
db_multi_exec(
1212
"UPDATE fv SET"
1213
" idm=coalesce(vfile.id,0),"
1214
" ridm=coalesce(vfile.rid,0),"
1215
" islinkm=coalesce(vfile.islink,0),"
1216
" isexe=coalesce(vfile.isexe,fv.isexe)"
1217
" FROM vfile"
1218
" WHERE vid=%d AND fnm=pathname",
1219
mid
1220
);
1221
1222
/*
1223
** Update the execute bit on files where it's changed from P->M but not P->V
1224
*/
1225
db_prepare(&q,
1226
"SELECT idv, fn, fv.isexe FROM fv, vfile p, vfile v"
1227
" WHERE p.id=idp AND v.id=idv AND fv.isexe!=p.isexe AND v.isexe=p.isexe"
1228
);
1229
while( db_step(&q)==SQLITE_ROW ){
1230
int idv = db_column_int(&q, 0);
1231
const char *zName = db_column_text(&q, 1);
1232
int isExe = db_column_int(&q, 2);
1233
fossil_print("%s %s\n", isExe ? "EXECUTABLE" : "UNEXEC", zName);
1234
if( !dryRunFlag ){
1235
char *zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1236
file_setexe(zFullPath, isExe);
1237
free(zFullPath);
1238
db_multi_exec("UPDATE vfile SET isexe=%d WHERE id=%d", isExe, idv);
1239
}
1240
}
1241
db_finalize(&q);
1242
if( debugFlag ){
1243
fossil_print("******** FV final *******\n");
1244
debug_fv_dump( debugFlag>=2 );
1245
}
1246
1247
/************************************************************************
1248
** All of the information needed to do the merge is now contained in the
1249
** FV table. Starting here, we begin to actually carry out the merge.
1250
**
1251
** Begin by constructing the localdb.mergestat table.
1252
*/
1253
merge_info_init();
1254
1255
/*
1256
** Find files that have changed from P->M but not P->V.
1257
** Copy the M content over into V.
1258
*/
1259
db_prepare(&q,
1260
/* 0 1 2 3 4 5 6 7 */
1261
"SELECT idv, ridm, fn, islinkm, fnp, ridp, ridv, fnm FROM fv"
1262
" WHERE idp>0 AND idv>0 AND idm>0"
1263
" AND ridm!=ridp AND ridv=ridp AND NOT chnged"
1264
);
1265
while( db_step(&q)==SQLITE_ROW ){
1266
int idv = db_column_int(&q, 0);
1267
int ridm = db_column_int(&q, 1);
1268
const char *zName = db_column_text(&q, 2);
1269
int islinkm = db_column_int(&q, 3);
1270
/* Copy content from idm over into idv. Overwrite idv. */
1271
fossil_print("UPDATE %s\n", zName);
1272
if( useUndo ) undo_save(zName);
1273
if( !dryRunFlag ){
1274
db_multi_exec(
1275
"UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
1276
" mhash=CASE WHEN rid<>%d"
1277
" THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
1278
" WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
1279
);
1280
vfile_to_disk(0, idv, 0, 0);
1281
}
1282
db_multi_exec(
1283
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr)"
1284
"VALUES('UPDATE',%Q,%d,%Q,%d,%Q,%d,%Q)",
1285
/* fnp */ db_column_text(&q, 4),
1286
/* ridp */ db_column_int(&q,5),
1287
/* fn */ zName,
1288
/* ridv */ db_column_int(&q,6),
1289
/* fnm */ db_column_text(&q, 7),
1290
/* ridm */ ridm,
1291
/* fnr */ zName
1292
);
1293
}
1294
db_finalize(&q);
1295
1296
/*
1297
** Do a three-way merge on files that have changes on both P->M and P->V.
1298
**
1299
** Proceed even if the file doesn't exist on P, just like the common ancestor
1300
** of M and V is an empty file. In this case, merge conflict marks will be
1301
** added to the file and user will be forced to take a decision.
1302
*/
1303
db_prepare(&q,
1304
/* 0 1 2 3 4 5 6 7 8 */
1305
"SELECT ridm, idv, ridp, ridv, %z, fn, isexe, islinkv, islinkm,"
1306
/* 9 10 11 */
1307
" fnp, fnm, chnged"
1308
" FROM fv"
1309
" WHERE idv>0 AND idm>0"
1310
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
1311
glob_expr("fv.fn", zBinGlob)
1312
);
1313
while( db_step(&q)==SQLITE_ROW ){
1314
int ridm = db_column_int(&q, 0);
1315
int idv = db_column_int(&q, 1);
1316
int ridp = db_column_int(&q, 2);
1317
int ridv = db_column_int(&q, 3);
1318
int isBinary = db_column_int(&q, 4);
1319
const char *zName = db_column_text(&q, 5);
1320
int isExe = db_column_int(&q, 6);
1321
int islinkv = db_column_int(&q, 7);
1322
int islinkm = db_column_int(&q, 8);
1323
int chnged = db_column_int(&q, 11);
1324
int rc;
1325
char *zFullPath;
1326
const char *zType = "MERGE";
1327
Blob m, p, r;
1328
/* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
1329
if( verboseFlag ){
1330
fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n",
1331
zName, ridp, ridm, ridv);
1332
}else{
1333
fossil_print("MERGE %s\n", zName);
1334
}
1335
if( islinkv || islinkm ){
1336
fossil_print("***** Cannot merge symlink %s\n", zName);
1337
nConflict++;
1338
db_multi_exec(
1339
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr,nc,msg)"
1340
"VALUES('ERROR',%Q,%d,%Q,%d,%Q,%d,%Q,1,'cannot merge symlink')",
1341
/* fnp */ db_column_text(&q, 9),
1342
/* ridp */ ridp,
1343
/* fn */ zName,
1344
/* ridv */ ridv,
1345
/* fnm */ db_column_text(&q, 10),
1346
/* ridm */ ridm,
1347
/* fnr */ zName
1348
);
1349
}else{
1350
i64 sz;
1351
const char *zErrMsg = 0;
1352
int nc = 0;
1353
1354
if( useUndo ) undo_save(zName);
1355
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1356
sz = file_size(zFullPath, ExtFILE);
1357
content_get(ridp, &p);
1358
content_get(ridm, &m);
1359
if( isBinary ){
1360
rc = -1;
1361
blob_zero(&r);
1362
}else{
1363
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
1364
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
1365
rc = merge_3way(&p, zFullPath, &m, &r, mergeFlags);
1366
}
1367
if( rc>=0 ){
1368
if( !dryRunFlag ){
1369
blob_write_to_file(&r, zFullPath);
1370
file_setexe(zFullPath, isExe);
1371
}
1372
db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
1373
if( rc>0 ){
1374
fossil_print("***** %d merge conflict%s in %s\n",
1375
rc, rc>1 ? "s" : "", zName);
1376
nConflict++;
1377
nc = rc;
1378
zErrMsg = "merge conflicts";
1379
zType = "CONFLICT";
1380
}
1381
}else{
1382
fossil_print("***** Cannot merge binary file %s\n", zName);
1383
nConflict++;
1384
nc = 1;
1385
zErrMsg = "cannot merge binary file";
1386
zType = "ERROR";
1387
}
1388
db_multi_exec(
1389
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
1390
"VALUES(%Q,%Q,%d,%Q,iif(%d,%d,NULL),iif(%d,%lld,NULL),%Q,%d,"
1391
"%Q,%d,%Q)",
1392
/* op */ zType,
1393
/* fnp */ db_column_text(&q, 9),
1394
/* ridp */ ridp,
1395
/* fn */ zName,
1396
/* ridv */ chnged==0, ridv,
1397
/* sz */ chnged!=0, sz,
1398
/* fnm */ db_column_text(&q, 10),
1399
/* ridm */ ridm,
1400
/* fnr */ zName,
1401
/* nc */ nc,
1402
/* msg */ zErrMsg
1403
);
1404
fossil_free(zFullPath);
1405
blob_reset(&p);
1406
blob_reset(&m);
1407
blob_reset(&r);
1408
}
1409
vmerge_insert(idv, ridm);
1410
}
1411
db_finalize(&q);
1412
1413
/*
1414
** Drop files that are in P and V but not in M
1415
*/
1416
db_prepare(&q,
1417
"SELECT idv, fn, chnged, ridv FROM fv"
1418
" WHERE idp>0 AND idv>0 AND idm=0"
1419
);
1420
while( db_step(&q)==SQLITE_ROW ){
1421
int idv = db_column_int(&q, 0);
1422
const char *zName = db_column_text(&q, 1);
1423
int chnged = db_column_int(&q, 2);
1424
int ridv = db_column_int(&q, 3);
1425
int sz = -1;
1426
const char *zErrMsg = 0;
1427
int nc = 0;
1428
/* Delete the file idv */
1429
fossil_print("DELETE %s\n", zName);
1430
if( chnged ){
1431
char *zFullPath;
1432
fossil_warning("WARNING: local edits lost for %s", zName);
1433
nConflict++;
1434
ridv = 0;
1435
nc = 1;
1436
zErrMsg = "local edits lost";
1437
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
1438
sz = file_size(zFullPath, ExtFILE);
1439
fossil_free(zFullPath);
1440
}
1441
if( useUndo ) undo_save(zName);
1442
db_multi_exec(
1443
"UPDATE vfile SET deleted=1 WHERE id=%d", idv
1444
);
1445
if( !dryRunFlag ){
1446
char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
1447
file_delete(zFullPath);
1448
free(zFullPath);
1449
}
1450
db_multi_exec(
1451
"INSERT INTO localdb.mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,nc,msg)"
1452
"VALUES('DELETE',NULL,NULL,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL),"
1453
"NULL,NULL,%d,%Q)",
1454
/* fn */ zName,
1455
/* ridv */ chnged==0, ridv,
1456
/* sz */ chnged!=0, sz,
1457
/* nc */ nc,
1458
/* msg */ zErrMsg
1459
);
1460
}
1461
db_finalize(&q);
1462
1463
/* For certain sets of renames (e.g. A -> B and B -> A), a file that is
1464
** being renamed must first be moved to a temporary location to avoid
1465
** being overwritten by another rename operation. A row is added to the
1466
** TMPRN table for each of these temporary renames.
1467
*/
1468
db_multi_exec(
1469
"DROP TABLE IF EXISTS tmprn;"
1470
"CREATE TEMP TABLE tmprn(fn UNIQUE, tmpfn);"
1471
);
1472
1473
/*
1474
** Rename files that have taken a rename on P->M but which keep the same
1475
** name on P->V. If a file is renamed on P->V only or on both P->V and
1476
** P->M then we retain the V name of the file.
1477
*/
1478
db_prepare(&q,
1479
"SELECT idv, fnp, fnm, isexe FROM fv"
1480
" WHERE idv>0 AND idp>0 AND idm>0 AND fnp=fn AND fnm!=fnp"
1481
);
1482
while( db_step(&q)==SQLITE_ROW ){
1483
int idv = db_column_int(&q, 0);
1484
const char *zOldName = db_column_text(&q, 1);
1485
const char *zNewName = db_column_text(&q, 2);
1486
int isExe = db_column_int(&q, 3);
1487
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
1488
if( useUndo ) undo_save(zOldName);
1489
if( useUndo ) undo_save(zNewName);
1490
db_multi_exec(
1491
"UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
1492
zOldName
1493
);
1494
db_multi_exec(
1495
"UPDATE vfile SET pathname=NULL, origname=pathname"
1496
" WHERE vid=%d AND pathname=%Q;"
1497
"UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
1498
" WHERE id=%d;",
1499
vid, zNewName, zNewName, idv
1500
);
1501
if( !dryRunFlag ){
1502
char *zFullOldPath, *zFullNewPath;
1503
zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
1504
if( !zFullOldPath ){
1505
zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
1506
}
1507
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
1508
if( file_size(zFullNewPath, RepoFILE)>=0 ){
1509
Blob tmpPath;
1510
file_tempname(&tmpPath, "", 0);
1511
db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)",
1512
zNewName, blob_str(&tmpPath));
1513
if( file_islink(zFullNewPath) ){
1514
symlink_copy(zFullNewPath, blob_str(&tmpPath));
1515
}else{
1516
file_copy(zFullNewPath, blob_str(&tmpPath));
1517
}
1518
blob_reset(&tmpPath);
1519
}
1520
if( file_islink(zFullOldPath) ){
1521
symlink_copy(zFullOldPath, zFullNewPath);
1522
}else{
1523
file_copy(zFullOldPath, zFullNewPath);
1524
}
1525
file_setexe(zFullNewPath, isExe);
1526
file_delete(zFullOldPath);
1527
free(zFullNewPath);
1528
free(zFullOldPath);
1529
}
1530
}
1531
db_finalize(&q);
1532
1533
/* A file that has been deleted and replaced by a renamed file will have a
1534
** NULL pathname. Change it to something that makes the output of "status"
1535
** and similar commands make sense for such files and that will (most likely)
1536
** not be an actual existing pathname.
1537
*/
1538
db_multi_exec(
1539
"UPDATE vfile SET pathname=origname || ' (overwritten by rename)'"
1540
" WHERE pathname IS NULL"
1541
);
1542
1543
/*
1544
** Insert into V any files that are not in V or P but are in M.
1545
*/
1546
db_prepare(&q,
1547
"SELECT idm, fnm, ridm FROM fv"
1548
" WHERE idp=0 AND idv=0 AND idm>0"
1549
);
1550
while( db_step(&q)==SQLITE_ROW ){
1551
int idm = db_column_int(&q, 0);
1552
const char *zName;
1553
char *zFullName;
1554
db_multi_exec(
1555
"REPLACE INTO vfile(vid,chnged,deleted,rid,mrid,"
1556
"isexe,islink,pathname,mhash)"
1557
" SELECT %d,%d,0,rid,mrid,isexe,islink,pathname,"
1558
"CASE WHEN rid<>mrid"
1559
" THEN (SELECT uuid FROM blob WHERE blob.rid=vfile.mrid) END "
1560
"FROM vfile WHERE id=%d",
1561
vid, integrateFlag?5:3, idm
1562
);
1563
zName = db_column_text(&q, 1);
1564
zFullName = mprintf("%s%s", g.zLocalRoot, zName);
1565
if( file_isfile_or_link(zFullName)
1566
&& !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
1567
/* Name of backup file with Original content */
1568
char *zOrig = file_newname(zFullName, "original", 1);
1569
/* Backup previously unmanaged file before being overwritten */
1570
file_copy(zFullName, zOrig);
1571
fossil_free(zOrig);
1572
fossil_print("ADDED %s (overwrites an unmanaged file)", zName);
1573
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
1574
fossil_print("\n");
1575
nOverwrite++;
1576
}else{
1577
fossil_print("ADDED %s\n", zName);
1578
}
1579
fossil_free(zFullName);
1580
db_multi_exec(
1581
"INSERT INTO mergestat(op,fnm,ridm,fnr)"
1582
"VALUES('ADDED',%Q,%d,%Q)",
1583
/* fnm */ zName,
1584
/* ridm */ db_column_int(&q,2),
1585
/* fnr */ zName
1586
);
1587
if( useUndo ) undo_save(zName);
1588
if( !dryRunFlag ){
1589
vfile_to_disk(0, idm, 0, 0);
1590
}
1591
}
1592
db_finalize(&q);
1593
1594
/* Report on conflicts
1595
*/
1596
if( nConflict ){
1597
fossil_warning("WARNING: %d merge conflicts", nConflict);
1598
if( bMultiMerge ){
1599
int i;
1600
Blob msg;
1601
blob_init(&msg, 0, 0);
1602
blob_appendf(&msg,
1603
"The following %ss were not attempted due to prior conflicts:",
1604
pickFlag ? "cherrypick" : backoutFlag ? "backout" : "merge"
1605
);
1606
for(i=2; i<g.argc; i++){
1607
blob_appendf(&msg, " %s", g.argv[i]);
1608
}
1609
fossil_warning("%s", blob_str(&msg));
1610
blob_zero(&msg);
1611
}
1612
}
1613
if( nOverwrite ){
1614
fossil_warning("WARNING: %d unmanaged files were overwritten",
1615
nOverwrite);
1616
}
1617
if( dryRunFlag && !bMultiMerge ){
1618
fossil_warning("REMINDER: this was a dry run -"
1619
" no files were actually changed.");
1620
}
1621
1622
/*
1623
** Clean up the mid and pid VFILE entries. Then commit the changes.
1624
*/
1625
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
1626
if( pickFlag ){
1627
vmerge_insert(-1, mid);
1628
/* For a cherrypick merge, make the default check-in comment the same
1629
** as the check-in comment on the check-in that is being merged in. */
1630
db_multi_exec(
1631
"REPLACE INTO vvar(name,value)"
1632
" SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
1633
" WHERE type='ci' AND objid=%d",
1634
mid
1635
);
1636
}else if( backoutFlag ){
1637
vmerge_insert(-2, pid);
1638
}else if( integrateFlag ){
1639
vmerge_insert(-4, mid);
1640
}else{
1641
vmerge_insert(0, mid);
1642
}
1643
if( bMultiMerge && nConflict==0 ){
1644
nMerge++;
1645
goto merge_next_child;
1646
}
1647
if( useUndo ) undo_finish();
1648
1649
db_end_transaction(dryRunFlag);
1650
}
1651

Keyboard Shortcuts

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