Fossil SCM

Merge the symlinks branch into trunk.

drh 2011-09-01 21:56 trunk merge
Commit e4f1c1fe950425bb56d8068a12be5a446df46554
D Makefile.in
-9
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +0,0 @@
1
- -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzzSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssitclsh -DFOSSIL_ENABLE_LEGACY_MV_RM=1@CFLAGS@@CFLAGS@Rdi make source
2
-#
3
-#
4
-OBJDIR = ./@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSILendif
5
-ifeq ($(findstriSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE$(MAKE) reconfig
6
-endif
7
-@AUTOREMAKE@ && $(MAKE)
8
-endif
9
-FOSSIL_ENABLE_M +
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +0,0 @@
1 -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzzSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssitclsh -DFOSSIL_ENABLE_LEGACY_MV_RM=1@CFLAGS@@CFLAGS@Rdi make source
2 #
3 #
4 OBJDIR = ./@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSILendif
5 ifeq ($(findstriSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE$(MAKE) reconfig
6 endif
7 @AUTOREMAKE@ && $(MAKE)
8 endif
9 FOSSIL_ENABLE_M +
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +0,0 @@
 
 
 
 
 
 
 
 
 
D Makefile.in
-9
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +0,0 @@
1
- -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzzSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssitclsh -DFOSSIL_ENABLE_LEGACY_MV_RM=1@CFLAGS@@CFLAGS@Rdi make source
2
-#
3
-#
4
-OBJDIR = ./@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSILendif
5
-ifeq ($(findstriSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE$(MAKE) reconfig
6
-endif
7
-@AUTOREMAKE@ && $(MAKE)
8
-endif
9
-FOSSIL_ENABLE_M +
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +0,0 @@
1 -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssil-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzzSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX-@FOSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -D_HAVE_SQLITE_ssitclsh -DFOSSIL_ENABLE_LEGACY_MV_RM=1@CFLAGS@@CFLAGS@Rdi make source
2 #
3 #
4 OBJDIR = ./@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE_ssil-@FOSSILendif
5 ifeq ($(findstriSSIL_CI_PFX@P@1BE,N:fossil:@FOSSIL_CI_PFX -fuzz -D_HAVE_SQLITE$(MAKE) reconfig
6 endif
7 @AUTOREMAKE@ && $(MAKE)
8 endif
9 FOSSIL_ENABLE_M +
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,9 +0,0 @@
 
 
 
 
 
 
 
 
 
+4 -4
--- src/add.c
+++ src/add.c
@@ -106,13 +106,13 @@
106106
db_multi_exec("UPDATE vfile SET deleted=0"
107107
" WHERE pathname=%Q COLLATE %s", zPath, zCollate);
108108
}else{
109109
char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
110110
db_multi_exec(
111
- "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe)"
112
- "VALUES(%d,0,0,0,%Q,%d)",
113
- vid, zPath, file_isexe(zFullname));
111
+ "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)"
112
+ "VALUES(%d,0,0,0,%Q,%d,%d)",
113
+ vid, zPath, file_isexe(zFullname), file_islink(zFullname));
114114
fossil_free(zFullname);
115115
}
116116
if( db_changes() ){
117117
fossil_print("ADDED %s\n", zPath);
118118
return 1;
@@ -426,11 +426,11 @@
426426
const char * zFile;
427427
const char * zPath;
428428
429429
zFile = db_column_text(&q, 0);
430430
zPath = db_column_text(&q, 1);
431
- if( !file_isfile(zPath) ){
431
+ if( !file_isfile_or_link(zPath) ){
432432
if( !isTest ){
433433
db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
434434
}
435435
fossil_print("DELETED %s\n", zFile);
436436
nDelete++;
437437
--- src/add.c
+++ src/add.c
@@ -106,13 +106,13 @@
106 db_multi_exec("UPDATE vfile SET deleted=0"
107 " WHERE pathname=%Q COLLATE %s", zPath, zCollate);
108 }else{
109 char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
110 db_multi_exec(
111 "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe)"
112 "VALUES(%d,0,0,0,%Q,%d)",
113 vid, zPath, file_isexe(zFullname));
114 fossil_free(zFullname);
115 }
116 if( db_changes() ){
117 fossil_print("ADDED %s\n", zPath);
118 return 1;
@@ -426,11 +426,11 @@
426 const char * zFile;
427 const char * zPath;
428
429 zFile = db_column_text(&q, 0);
430 zPath = db_column_text(&q, 1);
431 if( !file_isfile(zPath) ){
432 if( !isTest ){
433 db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
434 }
435 fossil_print("DELETED %s\n", zFile);
436 nDelete++;
437
--- src/add.c
+++ src/add.c
@@ -106,13 +106,13 @@
106 db_multi_exec("UPDATE vfile SET deleted=0"
107 " WHERE pathname=%Q COLLATE %s", zPath, zCollate);
108 }else{
109 char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
110 db_multi_exec(
111 "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)"
112 "VALUES(%d,0,0,0,%Q,%d,%d)",
113 vid, zPath, file_isexe(zFullname), file_islink(zFullname));
114 fossil_free(zFullname);
115 }
116 if( db_changes() ){
117 fossil_print("ADDED %s\n", zPath);
118 return 1;
@@ -426,11 +426,11 @@
426 const char * zFile;
427 const char * zPath;
428
429 zFile = db_column_text(&q, 0);
430 zPath = db_column_text(&q, 1);
431 if( !file_isfile_or_link(zPath) ){
432 if( !isTest ){
433 db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile);
434 }
435 fossil_print("DELETED %s\n", zFile);
436 nDelete++;
437
+26
--- src/blob.c
+++ src/blob.c
@@ -701,10 +701,36 @@
701701
if( got<size ){
702702
blob_resize(pBlob, got);
703703
}
704704
return got;
705705
}
706
+
707
+/*
708
+** Reads symlink destination path and puts int into blob.
709
+** Any prior content of the blob is discarded, not freed.
710
+**
711
+** Returns length of destination path.
712
+**
713
+** On windows, zeros blob and returns 0.
714
+*/
715
+int blob_read_link(Blob *pBlob, const char *zFilename){
716
+#if !defined(_WIN32)
717
+ char zBuf[1024];
718
+ ssize_t len = readlink(zFilename, zBuf, 1023);
719
+ if( len < 0 ){
720
+ fossil_panic("cannot read symbolic link %s", zFilename);
721
+ }
722
+ zBuf[len] = 0; /* null-terminate */
723
+ blob_zero(pBlob);
724
+ blob_appendf(pBlob, "%s", zBuf);
725
+ return len;
726
+#else
727
+ blob_zero(pBlob);
728
+ return 0;
729
+#endif
730
+}
731
+
706732
707733
/*
708734
** Write the content of a blob into a file.
709735
**
710736
** If the filename is blank or "-" then write to standard output.
711737
--- src/blob.c
+++ src/blob.c
@@ -701,10 +701,36 @@
701 if( got<size ){
702 blob_resize(pBlob, got);
703 }
704 return got;
705 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
706
707 /*
708 ** Write the content of a blob into a file.
709 **
710 ** If the filename is blank or "-" then write to standard output.
711
--- src/blob.c
+++ src/blob.c
@@ -701,10 +701,36 @@
701 if( got<size ){
702 blob_resize(pBlob, got);
703 }
704 return got;
705 }
706
707 /*
708 ** Reads symlink destination path and puts int into blob.
709 ** Any prior content of the blob is discarded, not freed.
710 **
711 ** Returns length of destination path.
712 **
713 ** On windows, zeros blob and returns 0.
714 */
715 int blob_read_link(Blob *pBlob, const char *zFilename){
716 #if !defined(_WIN32)
717 char zBuf[1024];
718 ssize_t len = readlink(zFilename, zBuf, 1023);
719 if( len < 0 ){
720 fossil_panic("cannot read symbolic link %s", zFilename);
721 }
722 zBuf[len] = 0; /* null-terminate */
723 blob_zero(pBlob);
724 blob_appendf(pBlob, "%s", zBuf);
725 return len;
726 #else
727 blob_zero(pBlob);
728 return 0;
729 #endif
730 }
731
732
733 /*
734 ** Write the content of a blob into a file.
735 **
736 ** If the filename is blank or "-" then write to standard output.
737
+18 -5
--- src/checkin.c
+++ src/checkin.c
@@ -64,11 +64,11 @@
6464
}
6565
}
6666
blob_append(report, zPrefix, nPrefix);
6767
if( isDeleted ){
6868
blob_appendf(report, "DELETED %s\n", zDisplayName);
69
- }else if( !file_isfile(zFullName) ){
69
+ }else if( !file_isfile_or_link(zFullName) ){
7070
if( file_access(zFullName, 0)==0 ){
7171
blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName);
7272
if( missingIsFatal ){
7373
fossil_warning("not a file: %s", zDisplayName);
7474
nErr++;
@@ -227,11 +227,11 @@
227227
fossil_print("%s\n", zPathname);
228228
}else if( isNew ){
229229
fossil_print("ADDED %s\n", zPathname);
230230
}else if( isDeleted ){
231231
fossil_print("DELETED %s\n", zPathname);
232
- }else if( !file_isfile(zFullName) ){
232
+ }else if( !file_isfile_or_link(zFullName) ){
233233
if( file_access(zFullName, 0)==0 ){
234234
fossil_print("NOT_A_FILE %s\n", zPathname);
235235
}else{
236236
fossil_print("MISSING %s\n", zPathname);
237237
}
@@ -635,11 +635,11 @@
635635
blob_appendf(pOut, "C %F\n", blob_str(pComment));
636636
zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
637637
blob_appendf(pOut, "D %s\n", zDate);
638638
zDate[10] = ' ';
639639
db_prepare(&q,
640
- "SELECT pathname, uuid, origname, blob.rid, isexe,"
640
+ "SELECT pathname, uuid, origname, blob.rid, isexe, islink,"
641641
" file_is_selected(vfile.id)"
642642
" FROM vfile JOIN blob ON vfile.mrid=blob.rid"
643643
" WHERE (NOT deleted OR NOT file_is_selected(vfile.id))"
644644
" AND vfile.vid=%d"
645645
" ORDER BY 1", vid);
@@ -650,11 +650,12 @@
650650
const char *zName = db_column_text(&q, 0);
651651
const char *zUuid = db_column_text(&q, 1);
652652
const char *zOrig = db_column_text(&q, 2);
653653
int frid = db_column_int(&q, 3);
654654
int isexe = db_column_int(&q, 4);
655
- int isSelected = db_column_int(&q, 5);
655
+ int isLink = db_column_int(&q, 5);
656
+ int isSelected = db_column_int(&q, 6);
656657
const char *zPerm;
657658
int cmp;
658659
#if !defined(_WIN32)
659660
/* For unix, extract the "executable" permission bit directly from
660661
** the filesystem. On windows, the "executable" bit is retained
@@ -661,13 +662,20 @@
661662
** unchanged from the original.
662663
*/
663664
blob_resize(&filename, nBasename);
664665
blob_append(&filename, zName, -1);
665666
isexe = file_isexe(blob_str(&filename));
667
+
668
+ /* For unix, check if the file on the filesystem is symlink.
669
+ ** On windows, the bit is retained unchanged from original.
670
+ */
671
+ isLink = file_islink(blob_str(&filename));
666672
#endif
667673
if( isexe ){
668674
zPerm = " x";
675
+ }else if( isLink ){
676
+ zPerm = " l"; /* note: symlinks don't have executable bit on unix */
669677
}else{
670678
zPerm = "";
671679
}
672680
if( !g.markPrivate ) content_make_public(frid);
673681
while( pFile && fossil_strcmp(pFile->zName,zName)<0 ){
@@ -1063,11 +1071,16 @@
10631071
zFullname = db_column_text(&q, 1);
10641072
rid = db_column_int(&q, 2);
10651073
crnlOk = db_column_int(&q, 3);
10661074
10671075
blob_zero(&content);
1068
- blob_read_from_file(&content, zFullname);
1076
+ if( file_islink(zFullname) ){
1077
+ /* Instead of file content, put link destination path */
1078
+ blob_read_link(&content, zFullname);
1079
+ }else{
1080
+ blob_read_from_file(&content, zFullname);
1081
+ }
10691082
if( !crnlOk ) cr_warning(&content, zFullname);
10701083
nrid = content_put(&content);
10711084
blob_reset(&content);
10721085
if( rid>0 ){
10731086
content_deltify(rid, nrid, 0);
10741087
--- src/checkin.c
+++ src/checkin.c
@@ -64,11 +64,11 @@
64 }
65 }
66 blob_append(report, zPrefix, nPrefix);
67 if( isDeleted ){
68 blob_appendf(report, "DELETED %s\n", zDisplayName);
69 }else if( !file_isfile(zFullName) ){
70 if( file_access(zFullName, 0)==0 ){
71 blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName);
72 if( missingIsFatal ){
73 fossil_warning("not a file: %s", zDisplayName);
74 nErr++;
@@ -227,11 +227,11 @@
227 fossil_print("%s\n", zPathname);
228 }else if( isNew ){
229 fossil_print("ADDED %s\n", zPathname);
230 }else if( isDeleted ){
231 fossil_print("DELETED %s\n", zPathname);
232 }else if( !file_isfile(zFullName) ){
233 if( file_access(zFullName, 0)==0 ){
234 fossil_print("NOT_A_FILE %s\n", zPathname);
235 }else{
236 fossil_print("MISSING %s\n", zPathname);
237 }
@@ -635,11 +635,11 @@
635 blob_appendf(pOut, "C %F\n", blob_str(pComment));
636 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
637 blob_appendf(pOut, "D %s\n", zDate);
638 zDate[10] = ' ';
639 db_prepare(&q,
640 "SELECT pathname, uuid, origname, blob.rid, isexe,"
641 " file_is_selected(vfile.id)"
642 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
643 " WHERE (NOT deleted OR NOT file_is_selected(vfile.id))"
644 " AND vfile.vid=%d"
645 " ORDER BY 1", vid);
@@ -650,11 +650,12 @@
650 const char *zName = db_column_text(&q, 0);
651 const char *zUuid = db_column_text(&q, 1);
652 const char *zOrig = db_column_text(&q, 2);
653 int frid = db_column_int(&q, 3);
654 int isexe = db_column_int(&q, 4);
655 int isSelected = db_column_int(&q, 5);
 
656 const char *zPerm;
657 int cmp;
658 #if !defined(_WIN32)
659 /* For unix, extract the "executable" permission bit directly from
660 ** the filesystem. On windows, the "executable" bit is retained
@@ -661,13 +662,20 @@
661 ** unchanged from the original.
662 */
663 blob_resize(&filename, nBasename);
664 blob_append(&filename, zName, -1);
665 isexe = file_isexe(blob_str(&filename));
 
 
 
 
 
666 #endif
667 if( isexe ){
668 zPerm = " x";
 
 
669 }else{
670 zPerm = "";
671 }
672 if( !g.markPrivate ) content_make_public(frid);
673 while( pFile && fossil_strcmp(pFile->zName,zName)<0 ){
@@ -1063,11 +1071,16 @@
1063 zFullname = db_column_text(&q, 1);
1064 rid = db_column_int(&q, 2);
1065 crnlOk = db_column_int(&q, 3);
1066
1067 blob_zero(&content);
1068 blob_read_from_file(&content, zFullname);
 
 
 
 
 
1069 if( !crnlOk ) cr_warning(&content, zFullname);
1070 nrid = content_put(&content);
1071 blob_reset(&content);
1072 if( rid>0 ){
1073 content_deltify(rid, nrid, 0);
1074
--- src/checkin.c
+++ src/checkin.c
@@ -64,11 +64,11 @@
64 }
65 }
66 blob_append(report, zPrefix, nPrefix);
67 if( isDeleted ){
68 blob_appendf(report, "DELETED %s\n", zDisplayName);
69 }else if( !file_isfile_or_link(zFullName) ){
70 if( file_access(zFullName, 0)==0 ){
71 blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName);
72 if( missingIsFatal ){
73 fossil_warning("not a file: %s", zDisplayName);
74 nErr++;
@@ -227,11 +227,11 @@
227 fossil_print("%s\n", zPathname);
228 }else if( isNew ){
229 fossil_print("ADDED %s\n", zPathname);
230 }else if( isDeleted ){
231 fossil_print("DELETED %s\n", zPathname);
232 }else if( !file_isfile_or_link(zFullName) ){
233 if( file_access(zFullName, 0)==0 ){
234 fossil_print("NOT_A_FILE %s\n", zPathname);
235 }else{
236 fossil_print("MISSING %s\n", zPathname);
237 }
@@ -635,11 +635,11 @@
635 blob_appendf(pOut, "C %F\n", blob_str(pComment));
636 zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now");
637 blob_appendf(pOut, "D %s\n", zDate);
638 zDate[10] = ' ';
639 db_prepare(&q,
640 "SELECT pathname, uuid, origname, blob.rid, isexe, islink,"
641 " file_is_selected(vfile.id)"
642 " FROM vfile JOIN blob ON vfile.mrid=blob.rid"
643 " WHERE (NOT deleted OR NOT file_is_selected(vfile.id))"
644 " AND vfile.vid=%d"
645 " ORDER BY 1", vid);
@@ -650,11 +650,12 @@
650 const char *zName = db_column_text(&q, 0);
651 const char *zUuid = db_column_text(&q, 1);
652 const char *zOrig = db_column_text(&q, 2);
653 int frid = db_column_int(&q, 3);
654 int isexe = db_column_int(&q, 4);
655 int isLink = db_column_int(&q, 5);
656 int isSelected = db_column_int(&q, 6);
657 const char *zPerm;
658 int cmp;
659 #if !defined(_WIN32)
660 /* For unix, extract the "executable" permission bit directly from
661 ** the filesystem. On windows, the "executable" bit is retained
@@ -661,13 +662,20 @@
662 ** unchanged from the original.
663 */
664 blob_resize(&filename, nBasename);
665 blob_append(&filename, zName, -1);
666 isexe = file_isexe(blob_str(&filename));
667
668 /* For unix, check if the file on the filesystem is symlink.
669 ** On windows, the bit is retained unchanged from original.
670 */
671 isLink = file_islink(blob_str(&filename));
672 #endif
673 if( isexe ){
674 zPerm = " x";
675 }else if( isLink ){
676 zPerm = " l"; /* note: symlinks don't have executable bit on unix */
677 }else{
678 zPerm = "";
679 }
680 if( !g.markPrivate ) content_make_public(frid);
681 while( pFile && fossil_strcmp(pFile->zName,zName)<0 ){
@@ -1063,11 +1071,16 @@
1071 zFullname = db_column_text(&q, 1);
1072 rid = db_column_int(&q, 2);
1073 crnlOk = db_column_int(&q, 3);
1074
1075 blob_zero(&content);
1076 if( file_islink(zFullname) ){
1077 /* Instead of file content, put link destination path */
1078 blob_read_link(&content, zFullname);
1079 }else{
1080 blob_read_from_file(&content, zFullname);
1081 }
1082 if( !crnlOk ) cr_warning(&content, zFullname);
1083 nrid = content_put(&content);
1084 blob_reset(&content);
1085 if( rid>0 ){
1086 content_deltify(rid, nrid, 0);
1087
+2 -1
--- src/checkout.c
+++ src/checkout.c
@@ -92,11 +92,12 @@
9292
db_reset(&s);
9393
}
9494
9595
/*
9696
** Set or clear the execute permission bit (as appropriate) for all
97
-** files in the current check-out.
97
+** files in the current check-out, and replace files that have
98
+** symlink bit with actual symlinks.
9899
*/
99100
void checkout_set_all_exe(int vid){
100101
Blob filename;
101102
int baseLen;
102103
Manifest *pManifest;
103104
--- src/checkout.c
+++ src/checkout.c
@@ -92,11 +92,12 @@
92 db_reset(&s);
93 }
94
95 /*
96 ** Set or clear the execute permission bit (as appropriate) for all
97 ** files in the current check-out.
 
98 */
99 void checkout_set_all_exe(int vid){
100 Blob filename;
101 int baseLen;
102 Manifest *pManifest;
103
--- src/checkout.c
+++ src/checkout.c
@@ -92,11 +92,12 @@
92 db_reset(&s);
93 }
94
95 /*
96 ** Set or clear the execute permission bit (as appropriate) for all
97 ** files in the current check-out, and replace files that have
98 ** symlink bit with actual symlinks.
99 */
100 void checkout_set_all_exe(int vid){
101 Blob filename;
102 int baseLen;
103 Manifest *pManifest;
104
+49 -32
--- src/db.c
+++ src/db.c
@@ -736,18 +736,35 @@
736736
}
737737
g.configOpen = 1;
738738
free(zDbName);
739739
}
740740
741
+
742
+/*
743
+ * * Returns TRUE if zTable exists in the local database.
744
+ */
745
+static int db_local_table_exists(const char *zTable){
746
+ return db_exists("SELECT 1 FROM %s.sqlite_master"
747
+ " WHERE name=='%s'",
748
+ db_name("localdb"), zTable);
749
+}
750
+
751
+/*
752
+** Returns TRUE if zColumn exists in zTable in the local database.
753
+*/
754
+static int db_local_column_exists(const char *zTable, const char *zColumn){
755
+ return db_exists("SELECT 1 FROM %s.sqlite_master"
756
+ " WHERE name=='%s' AND sql GLOB '* %s *'",
757
+ db_name("localdb"), zTable, zColumn);
758
+}
759
+
741760
/*
742761
** If zDbName is a valid local database file, open it and return
743762
** true. If it is not a valid local database file, return 0.
744763
*/
745764
static int isValidLocalDb(const char *zDbName){
746765
i64 lsize;
747
- int rc;
748
- sqlite3_stmt *pStmt;
749766
750767
if( file_access(zDbName, F_OK) ) return 0;
751768
lsize = file_size(zDbName);
752769
if( lsize%1024!=0 || lsize<4096 ) return 0;
753770
db_open_or_attach(zDbName, "localdb");
@@ -757,40 +774,31 @@
757774
758775
/* If the "isexe" column is missing from the vfile table, then
759776
** add it now. This code added on 2010-03-06. After all users have
760777
** upgraded, this code can be safely deleted.
761778
*/
762
- rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
763
- nPrepare++;
764
- sqlite3_finalize(pStmt);
765
- if( rc==SQLITE_ERROR ){
766
- sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
767
- }
768
-
769
-#if 0
770
- /* If the "mtime" column is missing from the vfile table, then
771
- ** add it now. This code added on 2008-12-06. After all users have
772
- ** upgraded, this code can be safely deleted.
773
- */
774
- rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
775
- sqlite3_finalize(pStmt);
776
- if( rc==SQLITE_ERROR ){
777
- sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
778
- }
779
-#endif
780
-
781
-#if 0
782
- /* If the "origname" column is missing from the vfile table, then
783
- ** add it now. This code added on 2008-11-09. After all users have
784
- ** upgraded, this code can be safely deleted.
785
- */
786
- rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
787
- sqlite3_finalize(pStmt);
788
- if( rc==SQLITE_ERROR ){
789
- sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
790
- }
791
-#endif
779
+ if( !db_local_column_exists("vfile", "isexe") )
780
+ db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
781
+
782
+ /* If "islink"/"isLink" columns are missing from tables, then
783
+ ** add them now. This code added on 2011-01-17 and 2011-08-27.
784
+ ** After all users have upgraded, this code can be safely deleted.
785
+ */
786
+ if( !db_local_column_exists("vfile", "islink") )
787
+ db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
788
+
789
+ if( !db_local_column_exists("stashfile", "isLink") &&
790
+ db_local_table_exists("stashfile") )
791
+ db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOLEAN DEFAULT 0");
792
+
793
+ if( !db_local_column_exists("undo", "isLink") &&
794
+ db_local_table_exists("undo") )
795
+ db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
796
+
797
+ if( !db_local_column_exists("undo_vfile", "islink") &&
798
+ db_local_table_exists("undo_vfile") )
799
+ db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
792800
793801
return 1;
794802
}
795803
796804
/*
@@ -866,10 +874,12 @@
866874
}
867875
}
868876
db_open_or_attach(zDbName, "repository");
869877
g.repositoryOpen = 1;
870878
g.zRepositoryName = mprintf("%s", zDbName);
879
+ /* Cache "allow-symlinks" option, because we'll need it on every stat call */
880
+ g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
871881
}
872882
873883
/*
874884
** Flags for the db_find_and_open_repository() function.
875885
*/
@@ -1747,10 +1757,11 @@
17471757
char const *def; /* Default value */
17481758
};
17491759
#endif /* INTERFACE */
17501760
struct stControlSettings const ctrlSettings[] = {
17511761
{ "access-log", 0, 0, 0, "off" },
1762
+ { "allow-symlinks",0, 0, 0, "off" },
17521763
{ "auto-captcha", "autocaptcha", 0, 0, "on" },
17531764
{ "auto-shun", 0, 0, 0, "on" },
17541765
{ "autosync", 0, 0, 0, "on" },
17551766
{ "binary-glob", 0, 32, 1, "" },
17561767
{ "clearsign", 0, 0, 0, "off" },
@@ -1799,10 +1810,16 @@
17991810
** file named .fossil-settings/PROPERTY in the checked out files, if that
18001811
** file exists.
18011812
**
18021813
** The "unset" command clears a property setting.
18031814
**
1815
+**
1816
+** allow-symlinks If enabled, don't follow symlinks, and instead treat
1817
+** them as symlinks on Unix. Has no effect on Windows
1818
+** (existing links in repository created on Unix become
1819
+** plain-text files with link destination path inside).
1820
+** Default: off
18041821
**
18051822
** auto-captcha If enabled, the Login page provides a button to
18061823
** fill in the captcha password. Default: on
18071824
**
18081825
** auto-shun If enabled, automatically pull the shunning list
18091826
--- src/db.c
+++ src/db.c
@@ -736,18 +736,35 @@
736 }
737 g.configOpen = 1;
738 free(zDbName);
739 }
740
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
741 /*
742 ** If zDbName is a valid local database file, open it and return
743 ** true. If it is not a valid local database file, return 0.
744 */
745 static int isValidLocalDb(const char *zDbName){
746 i64 lsize;
747 int rc;
748 sqlite3_stmt *pStmt;
749
750 if( file_access(zDbName, F_OK) ) return 0;
751 lsize = file_size(zDbName);
752 if( lsize%1024!=0 || lsize<4096 ) return 0;
753 db_open_or_attach(zDbName, "localdb");
@@ -757,40 +774,31 @@
757
758 /* If the "isexe" column is missing from the vfile table, then
759 ** add it now. This code added on 2010-03-06. After all users have
760 ** upgraded, this code can be safely deleted.
761 */
762 rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
763 nPrepare++;
764 sqlite3_finalize(pStmt);
765 if( rc==SQLITE_ERROR ){
766 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
767 }
768
769 #if 0
770 /* If the "mtime" column is missing from the vfile table, then
771 ** add it now. This code added on 2008-12-06. After all users have
772 ** upgraded, this code can be safely deleted.
773 */
774 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
775 sqlite3_finalize(pStmt);
776 if( rc==SQLITE_ERROR ){
777 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
778 }
779 #endif
780
781 #if 0
782 /* If the "origname" column is missing from the vfile table, then
783 ** add it now. This code added on 2008-11-09. After all users have
784 ** upgraded, this code can be safely deleted.
785 */
786 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
787 sqlite3_finalize(pStmt);
788 if( rc==SQLITE_ERROR ){
789 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
790 }
791 #endif
792
793 return 1;
794 }
795
796 /*
@@ -866,10 +874,12 @@
866 }
867 }
868 db_open_or_attach(zDbName, "repository");
869 g.repositoryOpen = 1;
870 g.zRepositoryName = mprintf("%s", zDbName);
 
 
871 }
872
873 /*
874 ** Flags for the db_find_and_open_repository() function.
875 */
@@ -1747,10 +1757,11 @@
1747 char const *def; /* Default value */
1748 };
1749 #endif /* INTERFACE */
1750 struct stControlSettings const ctrlSettings[] = {
1751 { "access-log", 0, 0, 0, "off" },
 
1752 { "auto-captcha", "autocaptcha", 0, 0, "on" },
1753 { "auto-shun", 0, 0, 0, "on" },
1754 { "autosync", 0, 0, 0, "on" },
1755 { "binary-glob", 0, 32, 1, "" },
1756 { "clearsign", 0, 0, 0, "off" },
@@ -1799,10 +1810,16 @@
1799 ** file named .fossil-settings/PROPERTY in the checked out files, if that
1800 ** file exists.
1801 **
1802 ** The "unset" command clears a property setting.
1803 **
 
 
 
 
 
 
1804 **
1805 ** auto-captcha If enabled, the Login page provides a button to
1806 ** fill in the captcha password. Default: on
1807 **
1808 ** auto-shun If enabled, automatically pull the shunning list
1809
--- src/db.c
+++ src/db.c
@@ -736,18 +736,35 @@
736 }
737 g.configOpen = 1;
738 free(zDbName);
739 }
740
741
742 /*
743 * * Returns TRUE if zTable exists in the local database.
744 */
745 static int db_local_table_exists(const char *zTable){
746 return db_exists("SELECT 1 FROM %s.sqlite_master"
747 " WHERE name=='%s'",
748 db_name("localdb"), zTable);
749 }
750
751 /*
752 ** Returns TRUE if zColumn exists in zTable in the local database.
753 */
754 static int db_local_column_exists(const char *zTable, const char *zColumn){
755 return db_exists("SELECT 1 FROM %s.sqlite_master"
756 " WHERE name=='%s' AND sql GLOB '* %s *'",
757 db_name("localdb"), zTable, zColumn);
758 }
759
760 /*
761 ** If zDbName is a valid local database file, open it and return
762 ** true. If it is not a valid local database file, return 0.
763 */
764 static int isValidLocalDb(const char *zDbName){
765 i64 lsize;
 
 
766
767 if( file_access(zDbName, F_OK) ) return 0;
768 lsize = file_size(zDbName);
769 if( lsize%1024!=0 || lsize<4096 ) return 0;
770 db_open_or_attach(zDbName, "localdb");
@@ -757,40 +774,31 @@
774
775 /* If the "isexe" column is missing from the vfile table, then
776 ** add it now. This code added on 2010-03-06. After all users have
777 ** upgraded, this code can be safely deleted.
778 */
779 if( !db_local_column_exists("vfile", "isexe") )
780 db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
781
782 /* If "islink"/"isLink" columns are missing from tables, then
783 ** add them now. This code added on 2011-01-17 and 2011-08-27.
784 ** After all users have upgraded, this code can be safely deleted.
785 */
786 if( !db_local_column_exists("vfile", "islink") )
787 db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
788
789 if( !db_local_column_exists("stashfile", "isLink") &&
790 db_local_table_exists("stashfile") )
791 db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOLEAN DEFAULT 0");
792
793 if( !db_local_column_exists("undo", "isLink") &&
794 db_local_table_exists("undo") )
795 db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
796
797 if( !db_local_column_exists("undo_vfile", "islink") &&
798 db_local_table_exists("undo_vfile") )
799 db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
 
 
 
 
 
 
 
 
 
800
801 return 1;
802 }
803
804 /*
@@ -866,10 +874,12 @@
874 }
875 }
876 db_open_or_attach(zDbName, "repository");
877 g.repositoryOpen = 1;
878 g.zRepositoryName = mprintf("%s", zDbName);
879 /* Cache "allow-symlinks" option, because we'll need it on every stat call */
880 g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
881 }
882
883 /*
884 ** Flags for the db_find_and_open_repository() function.
885 */
@@ -1747,10 +1757,11 @@
1757 char const *def; /* Default value */
1758 };
1759 #endif /* INTERFACE */
1760 struct stControlSettings const ctrlSettings[] = {
1761 { "access-log", 0, 0, 0, "off" },
1762 { "allow-symlinks",0, 0, 0, "off" },
1763 { "auto-captcha", "autocaptcha", 0, 0, "on" },
1764 { "auto-shun", 0, 0, 0, "on" },
1765 { "autosync", 0, 0, 0, "on" },
1766 { "binary-glob", 0, 32, 1, "" },
1767 { "clearsign", 0, 0, 0, "off" },
@@ -1799,10 +1810,16 @@
1810 ** file named .fossil-settings/PROPERTY in the checked out files, if that
1811 ** file exists.
1812 **
1813 ** The "unset" command clears a property setting.
1814 **
1815 **
1816 ** allow-symlinks If enabled, don't follow symlinks, and instead treat
1817 ** them as symlinks on Unix. Has no effect on Windows
1818 ** (existing links in repository created on Unix become
1819 ** plain-text files with link destination path inside).
1820 ** Default: off
1821 **
1822 ** auto-captcha If enabled, the Login page provides a button to
1823 ** fill in the captcha password. Default: on
1824 **
1825 ** auto-shun If enabled, automatically pull the shunning list
1826
+49 -32
--- src/db.c
+++ src/db.c
@@ -736,18 +736,35 @@
736736
}
737737
g.configOpen = 1;
738738
free(zDbName);
739739
}
740740
741
+
742
+/*
743
+ * * Returns TRUE if zTable exists in the local database.
744
+ */
745
+static int db_local_table_exists(const char *zTable){
746
+ return db_exists("SELECT 1 FROM %s.sqlite_master"
747
+ " WHERE name=='%s'",
748
+ db_name("localdb"), zTable);
749
+}
750
+
751
+/*
752
+** Returns TRUE if zColumn exists in zTable in the local database.
753
+*/
754
+static int db_local_column_exists(const char *zTable, const char *zColumn){
755
+ return db_exists("SELECT 1 FROM %s.sqlite_master"
756
+ " WHERE name=='%s' AND sql GLOB '* %s *'",
757
+ db_name("localdb"), zTable, zColumn);
758
+}
759
+
741760
/*
742761
** If zDbName is a valid local database file, open it and return
743762
** true. If it is not a valid local database file, return 0.
744763
*/
745764
static int isValidLocalDb(const char *zDbName){
746765
i64 lsize;
747
- int rc;
748
- sqlite3_stmt *pStmt;
749766
750767
if( file_access(zDbName, F_OK) ) return 0;
751768
lsize = file_size(zDbName);
752769
if( lsize%1024!=0 || lsize<4096 ) return 0;
753770
db_open_or_attach(zDbName, "localdb");
@@ -757,40 +774,31 @@
757774
758775
/* If the "isexe" column is missing from the vfile table, then
759776
** add it now. This code added on 2010-03-06. After all users have
760777
** upgraded, this code can be safely deleted.
761778
*/
762
- rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
763
- nPrepare++;
764
- sqlite3_finalize(pStmt);
765
- if( rc==SQLITE_ERROR ){
766
- sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
767
- }
768
-
769
-#if 0
770
- /* If the "mtime" column is missing from the vfile table, then
771
- ** add it now. This code added on 2008-12-06. After all users have
772
- ** upgraded, this code can be safely deleted.
773
- */
774
- rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
775
- sqlite3_finalize(pStmt);
776
- if( rc==SQLITE_ERROR ){
777
- sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
778
- }
779
-#endif
780
-
781
-#if 0
782
- /* If the "origname" column is missing from the vfile table, then
783
- ** add it now. This code added on 2008-11-09. After all users have
784
- ** upgraded, this code can be safely deleted.
785
- */
786
- rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
787
- sqlite3_finalize(pStmt);
788
- if( rc==SQLITE_ERROR ){
789
- sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
790
- }
791
-#endif
779
+ if( !db_local_column_exists("vfile", "isexe") )
780
+ db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
781
+
782
+ /* If "islink"/"isLink" columns are missing from tables, then
783
+ ** add them now. This code added on 2011-01-17 and 2011-08-27.
784
+ ** After all users have upgraded, this code can be safely deleted.
785
+ */
786
+ if( !db_local_column_exists("vfile", "islink") )
787
+ db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
788
+
789
+ if( !db_local_column_exists("stashfile", "isLink") &&
790
+ db_local_table_exists("stashfile") )
791
+ db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOLEAN DEFAULT 0");
792
+
793
+ if( !db_local_column_exists("undo", "isLink") &&
794
+ db_local_table_exists("undo") )
795
+ db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
796
+
797
+ if( !db_local_column_exists("undo_vfile", "islink") &&
798
+ db_local_table_exists("undo_vfile") )
799
+ db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
792800
793801
return 1;
794802
}
795803
796804
/*
@@ -866,10 +874,12 @@
866874
}
867875
}
868876
db_open_or_attach(zDbName, "repository");
869877
g.repositoryOpen = 1;
870878
g.zRepositoryName = mprintf("%s", zDbName);
879
+ /* Cache "allow-symlinks" option, because we'll need it on every stat call */
880
+ g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
871881
}
872882
873883
/*
874884
** Flags for the db_find_and_open_repository() function.
875885
*/
@@ -1747,10 +1757,11 @@
17471757
char const *def; /* Default value */
17481758
};
17491759
#endif /* INTERFACE */
17501760
struct stControlSettings const ctrlSettings[] = {
17511761
{ "access-log", 0, 0, 0, "off" },
1762
+ { "allow-symlinks",0, 0, 0, "off" },
17521763
{ "auto-captcha", "autocaptcha", 0, 0, "on" },
17531764
{ "auto-shun", 0, 0, 0, "on" },
17541765
{ "autosync", 0, 0, 0, "on" },
17551766
{ "binary-glob", 0, 32, 1, "" },
17561767
{ "clearsign", 0, 0, 0, "off" },
@@ -1799,10 +1810,16 @@
17991810
** file named .fossil-settings/PROPERTY in the checked out files, if that
18001811
** file exists.
18011812
**
18021813
** The "unset" command clears a property setting.
18031814
**
1815
+**
1816
+** allow-symlinks If enabled, don't follow symlinks, and instead treat
1817
+** them as symlinks on Unix. Has no effect on Windows
1818
+** (existing links in repository created on Unix become
1819
+** plain-text files with link destination path inside).
1820
+** Default: off
18041821
**
18051822
** auto-captcha If enabled, the Login page provides a button to
18061823
** fill in the captcha password. Default: on
18071824
**
18081825
** auto-shun If enabled, automatically pull the shunning list
18091826
--- src/db.c
+++ src/db.c
@@ -736,18 +736,35 @@
736 }
737 g.configOpen = 1;
738 free(zDbName);
739 }
740
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
741 /*
742 ** If zDbName is a valid local database file, open it and return
743 ** true. If it is not a valid local database file, return 0.
744 */
745 static int isValidLocalDb(const char *zDbName){
746 i64 lsize;
747 int rc;
748 sqlite3_stmt *pStmt;
749
750 if( file_access(zDbName, F_OK) ) return 0;
751 lsize = file_size(zDbName);
752 if( lsize%1024!=0 || lsize<4096 ) return 0;
753 db_open_or_attach(zDbName, "localdb");
@@ -757,40 +774,31 @@
757
758 /* If the "isexe" column is missing from the vfile table, then
759 ** add it now. This code added on 2010-03-06. After all users have
760 ** upgraded, this code can be safely deleted.
761 */
762 rc = sqlite3_prepare(g.db, "SELECT isexe FROM vfile", -1, &pStmt, 0);
763 nPrepare++;
764 sqlite3_finalize(pStmt);
765 if( rc==SQLITE_ERROR ){
766 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN isexe BOOLEAN", 0, 0, 0);
767 }
768
769 #if 0
770 /* If the "mtime" column is missing from the vfile table, then
771 ** add it now. This code added on 2008-12-06. After all users have
772 ** upgraded, this code can be safely deleted.
773 */
774 rc = sqlite3_prepare(g.db, "SELECT mtime FROM vfile", -1, &pStmt, 0);
775 sqlite3_finalize(pStmt);
776 if( rc==SQLITE_ERROR ){
777 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN mtime INTEGER", 0, 0, 0);
778 }
779 #endif
780
781 #if 0
782 /* If the "origname" column is missing from the vfile table, then
783 ** add it now. This code added on 2008-11-09. After all users have
784 ** upgraded, this code can be safely deleted.
785 */
786 rc = sqlite3_prepare(g.db, "SELECT origname FROM vfile", -1, &pStmt, 0);
787 sqlite3_finalize(pStmt);
788 if( rc==SQLITE_ERROR ){
789 sqlite3_exec(g.db, "ALTER TABLE vfile ADD COLUMN origname TEXT", 0, 0, 0);
790 }
791 #endif
792
793 return 1;
794 }
795
796 /*
@@ -866,10 +874,12 @@
866 }
867 }
868 db_open_or_attach(zDbName, "repository");
869 g.repositoryOpen = 1;
870 g.zRepositoryName = mprintf("%s", zDbName);
 
 
871 }
872
873 /*
874 ** Flags for the db_find_and_open_repository() function.
875 */
@@ -1747,10 +1757,11 @@
1747 char const *def; /* Default value */
1748 };
1749 #endif /* INTERFACE */
1750 struct stControlSettings const ctrlSettings[] = {
1751 { "access-log", 0, 0, 0, "off" },
 
1752 { "auto-captcha", "autocaptcha", 0, 0, "on" },
1753 { "auto-shun", 0, 0, 0, "on" },
1754 { "autosync", 0, 0, 0, "on" },
1755 { "binary-glob", 0, 32, 1, "" },
1756 { "clearsign", 0, 0, 0, "off" },
@@ -1799,10 +1810,16 @@
1799 ** file named .fossil-settings/PROPERTY in the checked out files, if that
1800 ** file exists.
1801 **
1802 ** The "unset" command clears a property setting.
1803 **
 
 
 
 
 
 
1804 **
1805 ** auto-captcha If enabled, the Login page provides a button to
1806 ** fill in the captcha password. Default: on
1807 **
1808 ** auto-shun If enabled, automatically pull the shunning list
1809
--- src/db.c
+++ src/db.c
@@ -736,18 +736,35 @@
736 }
737 g.configOpen = 1;
738 free(zDbName);
739 }
740
741
742 /*
743 * * Returns TRUE if zTable exists in the local database.
744 */
745 static int db_local_table_exists(const char *zTable){
746 return db_exists("SELECT 1 FROM %s.sqlite_master"
747 " WHERE name=='%s'",
748 db_name("localdb"), zTable);
749 }
750
751 /*
752 ** Returns TRUE if zColumn exists in zTable in the local database.
753 */
754 static int db_local_column_exists(const char *zTable, const char *zColumn){
755 return db_exists("SELECT 1 FROM %s.sqlite_master"
756 " WHERE name=='%s' AND sql GLOB '* %s *'",
757 db_name("localdb"), zTable, zColumn);
758 }
759
760 /*
761 ** If zDbName is a valid local database file, open it and return
762 ** true. If it is not a valid local database file, return 0.
763 */
764 static int isValidLocalDb(const char *zDbName){
765 i64 lsize;
 
 
766
767 if( file_access(zDbName, F_OK) ) return 0;
768 lsize = file_size(zDbName);
769 if( lsize%1024!=0 || lsize<4096 ) return 0;
770 db_open_or_attach(zDbName, "localdb");
@@ -757,40 +774,31 @@
774
775 /* If the "isexe" column is missing from the vfile table, then
776 ** add it now. This code added on 2010-03-06. After all users have
777 ** upgraded, this code can be safely deleted.
778 */
779 if( !db_local_column_exists("vfile", "isexe") )
780 db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
781
782 /* If "islink"/"isLink" columns are missing from tables, then
783 ** add them now. This code added on 2011-01-17 and 2011-08-27.
784 ** After all users have upgraded, this code can be safely deleted.
785 */
786 if( !db_local_column_exists("vfile", "islink") )
787 db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
788
789 if( !db_local_column_exists("stashfile", "isLink") &&
790 db_local_table_exists("stashfile") )
791 db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOLEAN DEFAULT 0");
792
793 if( !db_local_column_exists("undo", "isLink") &&
794 db_local_table_exists("undo") )
795 db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
796
797 if( !db_local_column_exists("undo_vfile", "islink") &&
798 db_local_table_exists("undo_vfile") )
799 db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
 
 
 
 
 
 
 
 
 
800
801 return 1;
802 }
803
804 /*
@@ -866,10 +874,12 @@
874 }
875 }
876 db_open_or_attach(zDbName, "repository");
877 g.repositoryOpen = 1;
878 g.zRepositoryName = mprintf("%s", zDbName);
879 /* Cache "allow-symlinks" option, because we'll need it on every stat call */
880 g.allowSymlinks = db_get_boolean("allow-symlinks", 0);
881 }
882
883 /*
884 ** Flags for the db_find_and_open_repository() function.
885 */
@@ -1747,10 +1757,11 @@
1757 char const *def; /* Default value */
1758 };
1759 #endif /* INTERFACE */
1760 struct stControlSettings const ctrlSettings[] = {
1761 { "access-log", 0, 0, 0, "off" },
1762 { "allow-symlinks",0, 0, 0, "off" },
1763 { "auto-captcha", "autocaptcha", 0, 0, "on" },
1764 { "auto-shun", 0, 0, 0, "on" },
1765 { "autosync", 0, 0, 0, "on" },
1766 { "binary-glob", 0, 32, 1, "" },
1767 { "clearsign", 0, 0, 0, "off" },
@@ -1799,10 +1810,16 @@
1810 ** file named .fossil-settings/PROPERTY in the checked out files, if that
1811 ** file exists.
1812 **
1813 ** The "unset" command clears a property setting.
1814 **
1815 **
1816 ** allow-symlinks If enabled, don't follow symlinks, and instead treat
1817 ** them as symlinks on Unix. Has no effect on Windows
1818 ** (existing links in repository created on Unix become
1819 ** plain-text files with link destination path inside).
1820 ** Default: off
1821 **
1822 ** auto-captcha If enabled, the Login page provides a button to
1823 ** fill in the captcha password. Default: on
1824 **
1825 ** auto-shun If enabled, automatically pull the shunning list
1826
+32 -10
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -74,11 +74,15 @@
7474
/* Read content of zFile2 into memory */
7575
blob_zero(&file2);
7676
if( file_size(zFile2)<0 ){
7777
zName2 = "/dev/null";
7878
}else{
79
- blob_read_from_file(&file2, zFile2);
79
+ if( file_islink(zFile2) ){
80
+ blob_read_link(&file2, zFile2);
81
+ }else{
82
+ blob_read_from_file(&file2, zFile2);
83
+ }
8084
zName2 = zName;
8185
}
8286
8387
/* Compute and output the differences */
8488
blob_zero(&out);
@@ -186,13 +190,18 @@
186190
int ignoreEolWs, /* Ignore whitespace changes at end of lines */
187191
const char *zFileTreeName
188192
){
189193
Blob fname;
190194
Blob content;
195
+ int isLink;
191196
file_tree_name(zFileTreeName, &fname, 1);
192
- historical_version_of_file(zFrom, blob_str(&fname), &content, 0, 0);
193
- diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
197
+ historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
198
+ if( !isLink != !file_islink(zFrom) ){
199
+ diff_printf("cannot compute difference between symlink and regular file\n");
200
+ }else{
201
+ diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
202
+ }
194203
blob_reset(&content);
195204
blob_reset(&fname);
196205
}
197206
198207
/*
@@ -222,32 +231,32 @@
222231
if( !is_a_version(rid) ){
223232
fossil_fatal("no such check-in: %s", zFrom);
224233
}
225234
load_vfile_from_rid(rid);
226235
blob_appendf(&sql,
227
- "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid"
236
+ "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid, v1.islink"
228237
" FROM vfile v1, vfile v2 "
229238
" WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
230239
" AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)"
231240
"UNION "
232
- "SELECT pathname, 1, 0, 0, 0"
241
+ "SELECT pathname, 1, 0, 0, 0, islink"
233242
" FROM vfile v1"
234243
" WHERE v1.vid=%d"
235244
" AND NOT EXISTS(SELECT 1 FROM vfile v2"
236245
" WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
237246
"UNION "
238
- "SELECT pathname, 0, 0, 1, 0"
247
+ "SELECT pathname, 0, 0, 1, 0, islink"
239248
" FROM vfile v2"
240249
" WHERE v2.vid=%d"
241250
" AND NOT EXISTS(SELECT 1 FROM vfile v1"
242251
" WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
243252
" ORDER BY 1",
244253
rid, vid, rid, vid, vid, rid
245254
);
246255
}else{
247256
blob_appendf(&sql,
248
- "SELECT pathname, deleted, chnged , rid==0, rid"
257
+ "SELECT pathname, deleted, chnged , rid==0, rid, islink"
249258
" FROM vfile"
250259
" WHERE vid=%d"
251260
" AND (deleted OR chnged OR rid==0)"
252261
" ORDER BY pathname",
253262
vid
@@ -258,10 +267,11 @@
258267
const char *zPathname = db_column_text(&q,0);
259268
int isDeleted = db_column_int(&q, 1);
260269
int isChnged = db_column_int(&q,2);
261270
int isNew = db_column_int(&q,3);
262271
int srcid = db_column_int(&q, 4);
272
+ int isLink = db_column_int(&q, 5);
263273
char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
264274
char *zToFree = zFullName;
265275
int showDiff = 1;
266276
if( isDeleted ){
267277
diff_printf("DELETED %s\n", zPathname);
@@ -278,10 +288,16 @@
278288
srcid = 0;
279289
if( !asNewFile ){ showDiff = 0; }
280290
}
281291
if( showDiff ){
282292
Blob content;
293
+ if( !isLink != !file_islink(zFullName) ){
294
+ diff_print_index(zPathname);
295
+ diff_printf("--- %s\n+++ %s\n", zPathname, zPathname);
296
+ diff_printf("cannot compute difference between symlink and regular file\n");
297
+ continue;
298
+ }
283299
if( srcid>0 ){
284300
content_get(srcid, &content);
285301
}else{
286302
blob_zero(&content);
287303
}
@@ -307,15 +323,21 @@
307323
const char *zFileTreeName
308324
){
309325
char *zName;
310326
Blob fname;
311327
Blob v1, v2;
328
+ int isLink1, isLink2;
312329
file_tree_name(zFileTreeName, &fname, 1);
313330
zName = blob_str(&fname);
314
- historical_version_of_file(zFrom, zName, &v1, 0, 0);
315
- historical_version_of_file(zTo, zName, &v2, 0, 0);
316
- diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
331
+ historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
332
+ historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
333
+ if( isLink1 != isLink2 ){
334
+ diff_printf("--- %s\n+++ %s\n", zName, zName);
335
+ diff_printf("cannot compute difference between symlink and regular file\n");
336
+ }else{
337
+ diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
338
+ }
317339
blob_reset(&v1);
318340
blob_reset(&v2);
319341
blob_reset(&fname);
320342
}
321343
322344
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -74,11 +74,15 @@
74 /* Read content of zFile2 into memory */
75 blob_zero(&file2);
76 if( file_size(zFile2)<0 ){
77 zName2 = "/dev/null";
78 }else{
79 blob_read_from_file(&file2, zFile2);
 
 
 
 
80 zName2 = zName;
81 }
82
83 /* Compute and output the differences */
84 blob_zero(&out);
@@ -186,13 +190,18 @@
186 int ignoreEolWs, /* Ignore whitespace changes at end of lines */
187 const char *zFileTreeName
188 ){
189 Blob fname;
190 Blob content;
 
191 file_tree_name(zFileTreeName, &fname, 1);
192 historical_version_of_file(zFrom, blob_str(&fname), &content, 0, 0);
193 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
 
 
 
 
194 blob_reset(&content);
195 blob_reset(&fname);
196 }
197
198 /*
@@ -222,32 +231,32 @@
222 if( !is_a_version(rid) ){
223 fossil_fatal("no such check-in: %s", zFrom);
224 }
225 load_vfile_from_rid(rid);
226 blob_appendf(&sql,
227 "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid"
228 " FROM vfile v1, vfile v2 "
229 " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
230 " AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)"
231 "UNION "
232 "SELECT pathname, 1, 0, 0, 0"
233 " FROM vfile v1"
234 " WHERE v1.vid=%d"
235 " AND NOT EXISTS(SELECT 1 FROM vfile v2"
236 " WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
237 "UNION "
238 "SELECT pathname, 0, 0, 1, 0"
239 " FROM vfile v2"
240 " WHERE v2.vid=%d"
241 " AND NOT EXISTS(SELECT 1 FROM vfile v1"
242 " WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
243 " ORDER BY 1",
244 rid, vid, rid, vid, vid, rid
245 );
246 }else{
247 blob_appendf(&sql,
248 "SELECT pathname, deleted, chnged , rid==0, rid"
249 " FROM vfile"
250 " WHERE vid=%d"
251 " AND (deleted OR chnged OR rid==0)"
252 " ORDER BY pathname",
253 vid
@@ -258,10 +267,11 @@
258 const char *zPathname = db_column_text(&q,0);
259 int isDeleted = db_column_int(&q, 1);
260 int isChnged = db_column_int(&q,2);
261 int isNew = db_column_int(&q,3);
262 int srcid = db_column_int(&q, 4);
 
263 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
264 char *zToFree = zFullName;
265 int showDiff = 1;
266 if( isDeleted ){
267 diff_printf("DELETED %s\n", zPathname);
@@ -278,10 +288,16 @@
278 srcid = 0;
279 if( !asNewFile ){ showDiff = 0; }
280 }
281 if( showDiff ){
282 Blob content;
 
 
 
 
 
 
283 if( srcid>0 ){
284 content_get(srcid, &content);
285 }else{
286 blob_zero(&content);
287 }
@@ -307,15 +323,21 @@
307 const char *zFileTreeName
308 ){
309 char *zName;
310 Blob fname;
311 Blob v1, v2;
 
312 file_tree_name(zFileTreeName, &fname, 1);
313 zName = blob_str(&fname);
314 historical_version_of_file(zFrom, zName, &v1, 0, 0);
315 historical_version_of_file(zTo, zName, &v2, 0, 0);
316 diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
 
 
 
 
 
317 blob_reset(&v1);
318 blob_reset(&v2);
319 blob_reset(&fname);
320 }
321
322
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -74,11 +74,15 @@
74 /* Read content of zFile2 into memory */
75 blob_zero(&file2);
76 if( file_size(zFile2)<0 ){
77 zName2 = "/dev/null";
78 }else{
79 if( file_islink(zFile2) ){
80 blob_read_link(&file2, zFile2);
81 }else{
82 blob_read_from_file(&file2, zFile2);
83 }
84 zName2 = zName;
85 }
86
87 /* Compute and output the differences */
88 blob_zero(&out);
@@ -186,13 +190,18 @@
190 int ignoreEolWs, /* Ignore whitespace changes at end of lines */
191 const char *zFileTreeName
192 ){
193 Blob fname;
194 Blob content;
195 int isLink;
196 file_tree_name(zFileTreeName, &fname, 1);
197 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
198 if( !isLink != !file_islink(zFrom) ){
199 diff_printf("cannot compute difference between symlink and regular file\n");
200 }else{
201 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
202 }
203 blob_reset(&content);
204 blob_reset(&fname);
205 }
206
207 /*
@@ -222,32 +231,32 @@
231 if( !is_a_version(rid) ){
232 fossil_fatal("no such check-in: %s", zFrom);
233 }
234 load_vfile_from_rid(rid);
235 blob_appendf(&sql,
236 "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid, v1.islink"
237 " FROM vfile v1, vfile v2 "
238 " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
239 " AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)"
240 "UNION "
241 "SELECT pathname, 1, 0, 0, 0, islink"
242 " FROM vfile v1"
243 " WHERE v1.vid=%d"
244 " AND NOT EXISTS(SELECT 1 FROM vfile v2"
245 " WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
246 "UNION "
247 "SELECT pathname, 0, 0, 1, 0, islink"
248 " FROM vfile v2"
249 " WHERE v2.vid=%d"
250 " AND NOT EXISTS(SELECT 1 FROM vfile v1"
251 " WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
252 " ORDER BY 1",
253 rid, vid, rid, vid, vid, rid
254 );
255 }else{
256 blob_appendf(&sql,
257 "SELECT pathname, deleted, chnged , rid==0, rid, islink"
258 " FROM vfile"
259 " WHERE vid=%d"
260 " AND (deleted OR chnged OR rid==0)"
261 " ORDER BY pathname",
262 vid
@@ -258,10 +267,11 @@
267 const char *zPathname = db_column_text(&q,0);
268 int isDeleted = db_column_int(&q, 1);
269 int isChnged = db_column_int(&q,2);
270 int isNew = db_column_int(&q,3);
271 int srcid = db_column_int(&q, 4);
272 int isLink = db_column_int(&q, 5);
273 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
274 char *zToFree = zFullName;
275 int showDiff = 1;
276 if( isDeleted ){
277 diff_printf("DELETED %s\n", zPathname);
@@ -278,10 +288,16 @@
288 srcid = 0;
289 if( !asNewFile ){ showDiff = 0; }
290 }
291 if( showDiff ){
292 Blob content;
293 if( !isLink != !file_islink(zFullName) ){
294 diff_print_index(zPathname);
295 diff_printf("--- %s\n+++ %s\n", zPathname, zPathname);
296 diff_printf("cannot compute difference between symlink and regular file\n");
297 continue;
298 }
299 if( srcid>0 ){
300 content_get(srcid, &content);
301 }else{
302 blob_zero(&content);
303 }
@@ -307,15 +323,21 @@
323 const char *zFileTreeName
324 ){
325 char *zName;
326 Blob fname;
327 Blob v1, v2;
328 int isLink1, isLink2;
329 file_tree_name(zFileTreeName, &fname, 1);
330 zName = blob_str(&fname);
331 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
332 historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
333 if( isLink1 != isLink2 ){
334 diff_printf("--- %s\n+++ %s\n", zName, zName);
335 diff_printf("cannot compute difference between symlink and regular file\n");
336 }else{
337 diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
338 }
339 blob_reset(&v1);
340 blob_reset(&v2);
341 blob_reset(&fname);
342 }
343
344
+9 -2
--- src/export.c
+++ src/export.c
@@ -269,12 +269,19 @@
269269
const char *zName = db_column_text(&q4,0);
270270
int zNew = db_column_int(&q4,1);
271271
int mPerm = db_column_int(&q4,2);
272272
if( zNew==0)
273273
printf("D %s\n", zName);
274
- else if( bag_find(&blobs, zNew) )
275
- printf("M %s :%d %s\n", mPerm ? "100755" : "100644", BLOBMARK(zNew), zName);
274
+ else if( bag_find(&blobs, zNew) ) {
275
+ const char *zPerm;
276
+ switch( mPerm ){
277
+ case PERM_LNK: zPerm = "120000"; break;
278
+ case PERM_EXE: zPerm = "100755"; break;
279
+ default: zPerm = "100644"; break;
280
+ }
281
+ printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName);
282
+ }
276283
}
277284
db_finalize(&q4);
278285
db_finalize(&q3);
279286
printf("\n");
280287
}
281288
--- src/export.c
+++ src/export.c
@@ -269,12 +269,19 @@
269 const char *zName = db_column_text(&q4,0);
270 int zNew = db_column_int(&q4,1);
271 int mPerm = db_column_int(&q4,2);
272 if( zNew==0)
273 printf("D %s\n", zName);
274 else if( bag_find(&blobs, zNew) )
275 printf("M %s :%d %s\n", mPerm ? "100755" : "100644", BLOBMARK(zNew), zName);
 
 
 
 
 
 
 
276 }
277 db_finalize(&q4);
278 db_finalize(&q3);
279 printf("\n");
280 }
281
--- src/export.c
+++ src/export.c
@@ -269,12 +269,19 @@
269 const char *zName = db_column_text(&q4,0);
270 int zNew = db_column_int(&q4,1);
271 int mPerm = db_column_int(&q4,2);
272 if( zNew==0)
273 printf("D %s\n", zName);
274 else if( bag_find(&blobs, zNew) ) {
275 const char *zPerm;
276 switch( mPerm ){
277 case PERM_LNK: zPerm = "120000"; break;
278 case PERM_EXE: zPerm = "100755"; break;
279 default: zPerm = "100644"; break;
280 }
281 printf("M %s :%d %s\n", zPerm, BLOBMARK(zNew), zName);
282 }
283 }
284 db_finalize(&q4);
285 db_finalize(&q3);
286 printf("\n");
287 }
288
+122 -6
--- src/file.c
+++ src/file.c
@@ -30,16 +30,26 @@
3030
**
3131
** Use _stati64 rather than stat on windows, in order to handle files
3232
** larger than 2GB.
3333
*/
3434
#if defined(_WIN32) && defined(__MSVCRT__)
35
- static struct _stati64 fileStat;
3635
# define stat _stati64
37
-#else
38
- static struct stat fileStat;
3936
#endif
4037
static int fileStatValid = 0;
38
+static struct stat fileStat;
39
+
40
+static int fossil_stat(const char *zFilename, struct stat *buf){
41
+#if !defined(_WIN32)
42
+ if( g.allowSymlinks ){
43
+ return lstat(zFilename, buf);
44
+ }else{
45
+ return stat(zFilename, buf);
46
+ }
47
+#else
48
+ return stat(zFilename, buf);
49
+#endif
50
+}
4151
4252
/*
4353
** Fill in the fileStat variable for the file named zFilename.
4454
** If zFilename==0, then use the previous value of fileStat if
4555
** there is a previous value.
@@ -50,11 +60,11 @@
5060
int rc = 0;
5161
if( zFilename==0 ){
5262
if( fileStatValid==0 ) rc = 1;
5363
}else{
5464
char *zMbcs = fossil_utf8_to_mbcs(zFilename);
55
- if( stat(zMbcs, &fileStat)!=0 ){
65
+ if( fossil_stat(zMbcs, &fileStat)!=0 ){
5666
fileStatValid = 0;
5767
rc = 1;
5868
}else{
5969
fileStatValid = 1;
6070
rc = 0;
@@ -80,18 +90,98 @@
8090
** recently stat-ed file.
8191
*/
8292
i64 file_mtime(const char *zFilename){
8393
return getStat(zFilename) ? -1 : fileStat.st_mtime;
8494
}
95
+
96
+/*
97
+** Return TRUE if the named file is an ordinary file or symlink
98
+** and symlinks are allowed.
99
+** Return false for directories, devices, fifos, etc.
100
+*/
101
+int file_isfile_or_link(const char *zFilename){
102
+#if !defined(_WIN32)
103
+ if ( g.allowSymlinks ){
104
+ return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode) || S_ISLNK(fileStat.st_mode);
105
+ }else{
106
+ return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
107
+ }
108
+#else
109
+ return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
110
+#endif
111
+}
85112
86113
/*
87114
** Return TRUE if the named file is an ordinary file. Return false
88115
** for directories, devices, fifos, symlinks, etc.
89116
*/
90117
int file_isfile(const char *zFilename){
91118
return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
92119
}
120
+
121
+/*
122
+** Return TRUE if the named file is a symlink and symlinks are allowed.
123
+** Return false for all other cases.
124
+**
125
+** On Windows, always return False.
126
+*/
127
+int file_islink(const char *zFilename){
128
+#if !defined(_WIN32)
129
+ if( g.allowSymlinks ){
130
+ return getStat(zFilename) ? 0 : S_ISLNK(fileStat.st_mode);
131
+ }else{
132
+ return 0;
133
+ }
134
+#else
135
+ return 0;
136
+#endif
137
+}
138
+
139
+/*
140
+** Create symlink to file on Unix, or plain-text file with
141
+** symlink target if "allow-symlinks" is off or we're on Windows.
142
+**
143
+** Arguments: target file (symlink will point to it), link file
144
+**/
145
+void create_symlink(const char *zTargetFile, const char *zLinkFile){
146
+#if !defined(_WIN32)
147
+ if( g.allowSymlinks ){
148
+ int i, nName;
149
+ char *zName, zBuf[1000];
150
+
151
+ nName = strlen(zLinkFile);
152
+ if( nName>=sizeof(zBuf) ){
153
+ zName = mprintf("%s", zLinkFile);
154
+ }else{
155
+ zName = zBuf;
156
+ memcpy(zName, zLinkFile, nName+1);
157
+ }
158
+ nName = file_simplify_name(zName, nName);
159
+ for(i=1; i<nName; i++){
160
+ if( zName[i]=='/' ){
161
+ zName[i] = 0;
162
+ if( file_mkdir(zName, 1) ){
163
+ fossil_fatal_recursive("unable to create directory %s", zName);
164
+ return;
165
+ }
166
+ zName[i] = '/';
167
+ }
168
+ }
169
+ if( zName!=zBuf ) free(zName);
170
+
171
+ if( symlink(zTargetFile, zName)!=0 ){
172
+ fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
173
+ }
174
+ }else
175
+#endif
176
+ {
177
+ Blob content;
178
+ blob_set(&content, zTargetFile);
179
+ blob_write_to_file(&content, zLinkFile);
180
+ blob_reset(&content);
181
+ }
182
+}
93183
94184
/*
95185
** Return TRUE if the named file is an executable. Return false
96186
** for directories, devices, fifos, symlinks, etc.
97187
*/
@@ -105,10 +195,22 @@
105195
#else
106196
return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
107197
#endif
108198
}
109199
200
+
201
+/*
202
+** Return file "permissions" (normal, executable, or symlink).
203
+*/
204
+int file_perm(const char *zFilename){
205
+ /*TODO(dchest): optimize by calling stat once.*/
206
+ if( file_isexe(zFilename) )
207
+ return PERM_EXE;
208
+ if( file_islink(zFilename) )
209
+ return PERM_LNK;
210
+ return PERM_REG;
211
+}
110212
111213
/*
112214
** Return 1 if zFilename is a directory. Return 0 if zFilename
113215
** does not exist. Return 2 if zFilename exists but is something
114216
** other than a directory.
@@ -122,11 +224,19 @@
122224
rc = getStat(zFN);
123225
free(zFN);
124226
}else{
125227
rc = getStat(0);
126228
}
229
+#if !defined(_WIN32)
230
+ if( g.allowSymlinks ){
231
+ return rc ? 0 : (S_ISDIR(fileStat.st_mode) && !S_ISLNK(fileStat.st_mode) ? 1 : 2);
232
+ }else{
233
+ return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
234
+ }
235
+#else
127236
return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
237
+#endif
128238
}
129239
130240
/*
131241
** Wrapper around the access() system call.
132242
*/
@@ -199,11 +309,11 @@
199309
*/
200310
int file_setexe(const char *zFilename, int onoff){
201311
int rc = 0;
202312
#if !defined(_WIN32)
203313
struct stat buf;
204
- if( stat(zFilename, &buf)!=0 ) return 0;
314
+ if( fossil_stat(zFilename, &buf)!=0 || S_ISLNK(buf.st_mode) ) return 0;
205315
if( onoff ){
206316
int targetMode = (buf.st_mode & 0444)>>2;
207317
if( (buf.st_mode & 0111)!=targetMode ){
208318
chmod(zFilename, buf.st_mode | targetMode);
209319
rc = 1;
@@ -469,10 +579,12 @@
469579
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName));
470580
fossil_print(" file_size = %s\n", zBuf);
471581
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName));
472582
fossil_print(" file_mtime = %s\n", zBuf);
473583
fossil_print(" file_isfile = %d\n", file_isfile(zName));
584
+ fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zName));
585
+ fossil_print(" file_islink = %d\n", file_islink(zName));
474586
fossil_print(" file_isexe = %d\n", file_isexe(zName));
475587
fossil_print(" file_isdir = %d\n", file_isdir(zName));
476588
}
477589
}
478590
@@ -739,11 +851,15 @@
739851
Blob onDisk;
740852
741853
iSize = file_size(zName);
742854
if( iSize<0 ) return 0;
743855
if( iSize!=blob_size(pContent) ) return 0;
744
- blob_read_from_file(&onDisk, zName);
856
+ if( file_islink(zName) ){
857
+ blob_read_link(&onDisk, zName);
858
+ }else{
859
+ blob_read_from_file(&onDisk, zName);
860
+ }
745861
rc = blob_compare(&onDisk, pContent);
746862
blob_reset(&onDisk);
747863
return rc==0;
748864
}
749865
750866
--- src/file.c
+++ src/file.c
@@ -30,16 +30,26 @@
30 **
31 ** Use _stati64 rather than stat on windows, in order to handle files
32 ** larger than 2GB.
33 */
34 #if defined(_WIN32) && defined(__MSVCRT__)
35 static struct _stati64 fileStat;
36 # define stat _stati64
37 #else
38 static struct stat fileStat;
39 #endif
40 static int fileStatValid = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
41
42 /*
43 ** Fill in the fileStat variable for the file named zFilename.
44 ** If zFilename==0, then use the previous value of fileStat if
45 ** there is a previous value.
@@ -50,11 +60,11 @@
50 int rc = 0;
51 if( zFilename==0 ){
52 if( fileStatValid==0 ) rc = 1;
53 }else{
54 char *zMbcs = fossil_utf8_to_mbcs(zFilename);
55 if( stat(zMbcs, &fileStat)!=0 ){
56 fileStatValid = 0;
57 rc = 1;
58 }else{
59 fileStatValid = 1;
60 rc = 0;
@@ -80,18 +90,98 @@
80 ** recently stat-ed file.
81 */
82 i64 file_mtime(const char *zFilename){
83 return getStat(zFilename) ? -1 : fileStat.st_mtime;
84 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
86 /*
87 ** Return TRUE if the named file is an ordinary file. Return false
88 ** for directories, devices, fifos, symlinks, etc.
89 */
90 int file_isfile(const char *zFilename){
91 return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
92 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
94 /*
95 ** Return TRUE if the named file is an executable. Return false
96 ** for directories, devices, fifos, symlinks, etc.
97 */
@@ -105,10 +195,22 @@
105 #else
106 return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
107 #endif
108 }
109
 
 
 
 
 
 
 
 
 
 
 
 
110
111 /*
112 ** Return 1 if zFilename is a directory. Return 0 if zFilename
113 ** does not exist. Return 2 if zFilename exists but is something
114 ** other than a directory.
@@ -122,11 +224,19 @@
122 rc = getStat(zFN);
123 free(zFN);
124 }else{
125 rc = getStat(0);
126 }
 
 
 
 
 
 
 
127 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
 
128 }
129
130 /*
131 ** Wrapper around the access() system call.
132 */
@@ -199,11 +309,11 @@
199 */
200 int file_setexe(const char *zFilename, int onoff){
201 int rc = 0;
202 #if !defined(_WIN32)
203 struct stat buf;
204 if( stat(zFilename, &buf)!=0 ) return 0;
205 if( onoff ){
206 int targetMode = (buf.st_mode & 0444)>>2;
207 if( (buf.st_mode & 0111)!=targetMode ){
208 chmod(zFilename, buf.st_mode | targetMode);
209 rc = 1;
@@ -469,10 +579,12 @@
469 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName));
470 fossil_print(" file_size = %s\n", zBuf);
471 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName));
472 fossil_print(" file_mtime = %s\n", zBuf);
473 fossil_print(" file_isfile = %d\n", file_isfile(zName));
 
 
474 fossil_print(" file_isexe = %d\n", file_isexe(zName));
475 fossil_print(" file_isdir = %d\n", file_isdir(zName));
476 }
477 }
478
@@ -739,11 +851,15 @@
739 Blob onDisk;
740
741 iSize = file_size(zName);
742 if( iSize<0 ) return 0;
743 if( iSize!=blob_size(pContent) ) return 0;
744 blob_read_from_file(&onDisk, zName);
 
 
 
 
745 rc = blob_compare(&onDisk, pContent);
746 blob_reset(&onDisk);
747 return rc==0;
748 }
749
750
--- src/file.c
+++ src/file.c
@@ -30,16 +30,26 @@
30 **
31 ** Use _stati64 rather than stat on windows, in order to handle files
32 ** larger than 2GB.
33 */
34 #if defined(_WIN32) && defined(__MSVCRT__)
 
35 # define stat _stati64
 
 
36 #endif
37 static int fileStatValid = 0;
38 static struct stat fileStat;
39
40 static int fossil_stat(const char *zFilename, struct stat *buf){
41 #if !defined(_WIN32)
42 if( g.allowSymlinks ){
43 return lstat(zFilename, buf);
44 }else{
45 return stat(zFilename, buf);
46 }
47 #else
48 return stat(zFilename, buf);
49 #endif
50 }
51
52 /*
53 ** Fill in the fileStat variable for the file named zFilename.
54 ** If zFilename==0, then use the previous value of fileStat if
55 ** there is a previous value.
@@ -50,11 +60,11 @@
60 int rc = 0;
61 if( zFilename==0 ){
62 if( fileStatValid==0 ) rc = 1;
63 }else{
64 char *zMbcs = fossil_utf8_to_mbcs(zFilename);
65 if( fossil_stat(zMbcs, &fileStat)!=0 ){
66 fileStatValid = 0;
67 rc = 1;
68 }else{
69 fileStatValid = 1;
70 rc = 0;
@@ -80,18 +90,98 @@
90 ** recently stat-ed file.
91 */
92 i64 file_mtime(const char *zFilename){
93 return getStat(zFilename) ? -1 : fileStat.st_mtime;
94 }
95
96 /*
97 ** Return TRUE if the named file is an ordinary file or symlink
98 ** and symlinks are allowed.
99 ** Return false for directories, devices, fifos, etc.
100 */
101 int file_isfile_or_link(const char *zFilename){
102 #if !defined(_WIN32)
103 if ( g.allowSymlinks ){
104 return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode) || S_ISLNK(fileStat.st_mode);
105 }else{
106 return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
107 }
108 #else
109 return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
110 #endif
111 }
112
113 /*
114 ** Return TRUE if the named file is an ordinary file. Return false
115 ** for directories, devices, fifos, symlinks, etc.
116 */
117 int file_isfile(const char *zFilename){
118 return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
119 }
120
121 /*
122 ** Return TRUE if the named file is a symlink and symlinks are allowed.
123 ** Return false for all other cases.
124 **
125 ** On Windows, always return False.
126 */
127 int file_islink(const char *zFilename){
128 #if !defined(_WIN32)
129 if( g.allowSymlinks ){
130 return getStat(zFilename) ? 0 : S_ISLNK(fileStat.st_mode);
131 }else{
132 return 0;
133 }
134 #else
135 return 0;
136 #endif
137 }
138
139 /*
140 ** Create symlink to file on Unix, or plain-text file with
141 ** symlink target if "allow-symlinks" is off or we're on Windows.
142 **
143 ** Arguments: target file (symlink will point to it), link file
144 **/
145 void create_symlink(const char *zTargetFile, const char *zLinkFile){
146 #if !defined(_WIN32)
147 if( g.allowSymlinks ){
148 int i, nName;
149 char *zName, zBuf[1000];
150
151 nName = strlen(zLinkFile);
152 if( nName>=sizeof(zBuf) ){
153 zName = mprintf("%s", zLinkFile);
154 }else{
155 zName = zBuf;
156 memcpy(zName, zLinkFile, nName+1);
157 }
158 nName = file_simplify_name(zName, nName);
159 for(i=1; i<nName; i++){
160 if( zName[i]=='/' ){
161 zName[i] = 0;
162 if( file_mkdir(zName, 1) ){
163 fossil_fatal_recursive("unable to create directory %s", zName);
164 return;
165 }
166 zName[i] = '/';
167 }
168 }
169 if( zName!=zBuf ) free(zName);
170
171 if( symlink(zTargetFile, zName)!=0 ){
172 fossil_fatal_recursive("unable to create symlink \"%s\"", zName);
173 }
174 }else
175 #endif
176 {
177 Blob content;
178 blob_set(&content, zTargetFile);
179 blob_write_to_file(&content, zLinkFile);
180 blob_reset(&content);
181 }
182 }
183
184 /*
185 ** Return TRUE if the named file is an executable. Return false
186 ** for directories, devices, fifos, symlinks, etc.
187 */
@@ -105,10 +195,22 @@
195 #else
196 return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
197 #endif
198 }
199
200
201 /*
202 ** Return file "permissions" (normal, executable, or symlink).
203 */
204 int file_perm(const char *zFilename){
205 /*TODO(dchest): optimize by calling stat once.*/
206 if( file_isexe(zFilename) )
207 return PERM_EXE;
208 if( file_islink(zFilename) )
209 return PERM_LNK;
210 return PERM_REG;
211 }
212
213 /*
214 ** Return 1 if zFilename is a directory. Return 0 if zFilename
215 ** does not exist. Return 2 if zFilename exists but is something
216 ** other than a directory.
@@ -122,11 +224,19 @@
224 rc = getStat(zFN);
225 free(zFN);
226 }else{
227 rc = getStat(0);
228 }
229 #if !defined(_WIN32)
230 if( g.allowSymlinks ){
231 return rc ? 0 : (S_ISDIR(fileStat.st_mode) && !S_ISLNK(fileStat.st_mode) ? 1 : 2);
232 }else{
233 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
234 }
235 #else
236 return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
237 #endif
238 }
239
240 /*
241 ** Wrapper around the access() system call.
242 */
@@ -199,11 +309,11 @@
309 */
310 int file_setexe(const char *zFilename, int onoff){
311 int rc = 0;
312 #if !defined(_WIN32)
313 struct stat buf;
314 if( fossil_stat(zFilename, &buf)!=0 || S_ISLNK(buf.st_mode) ) return 0;
315 if( onoff ){
316 int targetMode = (buf.st_mode & 0444)>>2;
317 if( (buf.st_mode & 0111)!=targetMode ){
318 chmod(zFilename, buf.st_mode | targetMode);
319 rc = 1;
@@ -469,10 +579,12 @@
579 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName));
580 fossil_print(" file_size = %s\n", zBuf);
581 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName));
582 fossil_print(" file_mtime = %s\n", zBuf);
583 fossil_print(" file_isfile = %d\n", file_isfile(zName));
584 fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zName));
585 fossil_print(" file_islink = %d\n", file_islink(zName));
586 fossil_print(" file_isexe = %d\n", file_isexe(zName));
587 fossil_print(" file_isdir = %d\n", file_isdir(zName));
588 }
589 }
590
@@ -739,11 +851,15 @@
851 Blob onDisk;
852
853 iSize = file_size(zName);
854 if( iSize<0 ) return 0;
855 if( iSize!=blob_size(pContent) ) return 0;
856 if( file_islink(zName) ){
857 blob_read_link(&onDisk, zName);
858 }else{
859 blob_read_from_file(&onDisk, zName);
860 }
861 rc = blob_compare(&onDisk, pContent);
862 blob_reset(&onDisk);
863 return rc==0;
864 }
865
866
+1 -1
--- src/finfo.c
+++ src/finfo.c
@@ -99,11 +99,11 @@
9999
Blob fname;
100100
const char *zRevision = find_option("revision", "r", 1);
101101
102102
file_tree_name(g.argv[2], &fname, 1);
103103
if( zRevision ){
104
- historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0);
104
+ historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0, 0);
105105
}else{
106106
int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname);
107107
if( rid==0 ){
108108
fossil_fatal("no history for file: %b", &fname);
109109
}
110110
--- src/finfo.c
+++ src/finfo.c
@@ -99,11 +99,11 @@
99 Blob fname;
100 const char *zRevision = find_option("revision", "r", 1);
101
102 file_tree_name(g.argv[2], &fname, 1);
103 if( zRevision ){
104 historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0);
105 }else{
106 int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname);
107 if( rid==0 ){
108 fossil_fatal("no history for file: %b", &fname);
109 }
110
--- src/finfo.c
+++ src/finfo.c
@@ -99,11 +99,11 @@
99 Blob fname;
100 const char *zRevision = find_option("revision", "r", 1);
101
102 file_tree_name(g.argv[2], &fname, 1);
103 if( zRevision ){
104 historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0, 0);
105 }else{
106 int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname);
107 if( rid==0 ){
108 fossil_fatal("no history for file: %b", &fname);
109 }
110
+12
--- src/import.c
+++ src/import.c
@@ -30,10 +30,11 @@
3030
char *zName; /* Name of a file */
3131
char *zUuid; /* UUID of the file */
3232
char *zPrior; /* Prior name if the name was changed */
3333
char isFrom; /* True if obtained from the parent */
3434
char isExe; /* True if executable */
35
+ char isLink; /* True if symlink */
3536
};
3637
#endif
3738
3839
3940
/*
@@ -58,10 +59,11 @@
5859
char **azMerge; /* Merge values */
5960
int nFile; /* Number of aFile values */
6061
int nFileAlloc; /* Number of slots in aFile[] */
6162
ImportFile *aFile; /* Information about files in a commit */
6263
int fromLoaded; /* True zFrom content loaded into aFile[] */
64
+ int hasLinks; /* True if git repository contains symlinks */
6365
int tagCommit; /* True if the commit adds a tag */
6466
} gg;
6567
6668
/*
6769
** Duplicate a string.
@@ -242,10 +244,13 @@
242244
const char *zUuid = gg.aFile[i].zUuid;
243245
if( zUuid==0 ) continue;
244246
blob_appendf(&record, "F %F %s", gg.aFile[i].zName, zUuid);
245247
if( gg.aFile[i].isExe ){
246248
blob_append(&record, " x\n", 3);
249
+ }else if( gg.aFile[i].isLink ){
250
+ blob_append(&record, " l\n", 3);
251
+ gg.hasLinks = 1;
247252
}else{
248253
blob_append(&record, "\n", 1);
249254
}
250255
}
251256
if( gg.zFrom ){
@@ -421,10 +426,11 @@
421426
manifest_file_rewind(p);
422427
while( (pOld = manifest_file_next(p, 0))!=0 ){
423428
pNew = import_add_file();
424429
pNew->zName = fossil_strdup(pOld->zName);
425430
pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0;
431
+ pNew->isLink = pOld->zPerm && strstr(pOld->zPerm, "l")!=0;
426432
pNew->zUuid = fossil_strdup(pOld->zUuid);
427433
pNew->isFrom = 1;
428434
}
429435
manifest_destroy(p);
430436
}
@@ -597,10 +603,11 @@
597603
if( pFile==0 ){
598604
pFile = import_add_file();
599605
pFile->zName = fossil_strdup(zName);
600606
}
601607
pFile->isExe = (fossil_strcmp(zPerm, "100755")==0);
608
+ pFile->isLink = (fossil_strcmp(zPerm, "120000")==0);
602609
fossil_free(pFile->zUuid);
603610
pFile->zUuid = resolve_committish(zUuid);
604611
pFile->isFrom = 0;
605612
}else
606613
if( memcmp(zLine, "D ", 2)==0 ){
@@ -634,10 +641,11 @@
634641
pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]);
635642
}else{
636643
pNew->zName = fossil_strdup(pFile->zName);
637644
}
638645
pNew->isExe = pFile->isExe;
646
+ pNew->isLink = pFile->isLink;
639647
pNew->zUuid = fossil_strdup(pFile->zUuid);
640648
pNew->isFrom = 0;
641649
}
642650
}else
643651
if( memcmp(zLine, "R ", 2)==0 ){
@@ -657,10 +665,11 @@
657665
}else{
658666
pNew->zName = fossil_strdup(pFile->zName);
659667
}
660668
pNew->zPrior = pFile->zName;
661669
pNew->isExe = pFile->isExe;
670
+ pNew->isLink = pFile->isLink;
662671
pNew->zUuid = pFile->zUuid;
663672
pNew->isFrom = 0;
664673
gg.nFile--;
665674
*pFile = *pNew;
666675
memset(pNew, 0, sizeof(*pNew));
@@ -677,10 +686,13 @@
677686
{
678687
goto malformed_line;
679688
}
680689
}
681690
gg.xFinish();
691
+ if( gg.hasLinks ){
692
+ db_set_int("allow-symlinks", 1, 0);
693
+ }
682694
import_reset(1);
683695
return;
684696
685697
malformed_line:
686698
trim_newline(zLine);
687699
--- src/import.c
+++ src/import.c
@@ -30,10 +30,11 @@
30 char *zName; /* Name of a file */
31 char *zUuid; /* UUID of the file */
32 char *zPrior; /* Prior name if the name was changed */
33 char isFrom; /* True if obtained from the parent */
34 char isExe; /* True if executable */
 
35 };
36 #endif
37
38
39 /*
@@ -58,10 +59,11 @@
58 char **azMerge; /* Merge values */
59 int nFile; /* Number of aFile values */
60 int nFileAlloc; /* Number of slots in aFile[] */
61 ImportFile *aFile; /* Information about files in a commit */
62 int fromLoaded; /* True zFrom content loaded into aFile[] */
 
63 int tagCommit; /* True if the commit adds a tag */
64 } gg;
65
66 /*
67 ** Duplicate a string.
@@ -242,10 +244,13 @@
242 const char *zUuid = gg.aFile[i].zUuid;
243 if( zUuid==0 ) continue;
244 blob_appendf(&record, "F %F %s", gg.aFile[i].zName, zUuid);
245 if( gg.aFile[i].isExe ){
246 blob_append(&record, " x\n", 3);
 
 
 
247 }else{
248 blob_append(&record, "\n", 1);
249 }
250 }
251 if( gg.zFrom ){
@@ -421,10 +426,11 @@
421 manifest_file_rewind(p);
422 while( (pOld = manifest_file_next(p, 0))!=0 ){
423 pNew = import_add_file();
424 pNew->zName = fossil_strdup(pOld->zName);
425 pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0;
 
426 pNew->zUuid = fossil_strdup(pOld->zUuid);
427 pNew->isFrom = 1;
428 }
429 manifest_destroy(p);
430 }
@@ -597,10 +603,11 @@
597 if( pFile==0 ){
598 pFile = import_add_file();
599 pFile->zName = fossil_strdup(zName);
600 }
601 pFile->isExe = (fossil_strcmp(zPerm, "100755")==0);
 
602 fossil_free(pFile->zUuid);
603 pFile->zUuid = resolve_committish(zUuid);
604 pFile->isFrom = 0;
605 }else
606 if( memcmp(zLine, "D ", 2)==0 ){
@@ -634,10 +641,11 @@
634 pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]);
635 }else{
636 pNew->zName = fossil_strdup(pFile->zName);
637 }
638 pNew->isExe = pFile->isExe;
 
639 pNew->zUuid = fossil_strdup(pFile->zUuid);
640 pNew->isFrom = 0;
641 }
642 }else
643 if( memcmp(zLine, "R ", 2)==0 ){
@@ -657,10 +665,11 @@
657 }else{
658 pNew->zName = fossil_strdup(pFile->zName);
659 }
660 pNew->zPrior = pFile->zName;
661 pNew->isExe = pFile->isExe;
 
662 pNew->zUuid = pFile->zUuid;
663 pNew->isFrom = 0;
664 gg.nFile--;
665 *pFile = *pNew;
666 memset(pNew, 0, sizeof(*pNew));
@@ -677,10 +686,13 @@
677 {
678 goto malformed_line;
679 }
680 }
681 gg.xFinish();
 
 
 
682 import_reset(1);
683 return;
684
685 malformed_line:
686 trim_newline(zLine);
687
--- src/import.c
+++ src/import.c
@@ -30,10 +30,11 @@
30 char *zName; /* Name of a file */
31 char *zUuid; /* UUID of the file */
32 char *zPrior; /* Prior name if the name was changed */
33 char isFrom; /* True if obtained from the parent */
34 char isExe; /* True if executable */
35 char isLink; /* True if symlink */
36 };
37 #endif
38
39
40 /*
@@ -58,10 +59,11 @@
59 char **azMerge; /* Merge values */
60 int nFile; /* Number of aFile values */
61 int nFileAlloc; /* Number of slots in aFile[] */
62 ImportFile *aFile; /* Information about files in a commit */
63 int fromLoaded; /* True zFrom content loaded into aFile[] */
64 int hasLinks; /* True if git repository contains symlinks */
65 int tagCommit; /* True if the commit adds a tag */
66 } gg;
67
68 /*
69 ** Duplicate a string.
@@ -242,10 +244,13 @@
244 const char *zUuid = gg.aFile[i].zUuid;
245 if( zUuid==0 ) continue;
246 blob_appendf(&record, "F %F %s", gg.aFile[i].zName, zUuid);
247 if( gg.aFile[i].isExe ){
248 blob_append(&record, " x\n", 3);
249 }else if( gg.aFile[i].isLink ){
250 blob_append(&record, " l\n", 3);
251 gg.hasLinks = 1;
252 }else{
253 blob_append(&record, "\n", 1);
254 }
255 }
256 if( gg.zFrom ){
@@ -421,10 +426,11 @@
426 manifest_file_rewind(p);
427 while( (pOld = manifest_file_next(p, 0))!=0 ){
428 pNew = import_add_file();
429 pNew->zName = fossil_strdup(pOld->zName);
430 pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0;
431 pNew->isLink = pOld->zPerm && strstr(pOld->zPerm, "l")!=0;
432 pNew->zUuid = fossil_strdup(pOld->zUuid);
433 pNew->isFrom = 1;
434 }
435 manifest_destroy(p);
436 }
@@ -597,10 +603,11 @@
603 if( pFile==0 ){
604 pFile = import_add_file();
605 pFile->zName = fossil_strdup(zName);
606 }
607 pFile->isExe = (fossil_strcmp(zPerm, "100755")==0);
608 pFile->isLink = (fossil_strcmp(zPerm, "120000")==0);
609 fossil_free(pFile->zUuid);
610 pFile->zUuid = resolve_committish(zUuid);
611 pFile->isFrom = 0;
612 }else
613 if( memcmp(zLine, "D ", 2)==0 ){
@@ -634,10 +641,11 @@
641 pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]);
642 }else{
643 pNew->zName = fossil_strdup(pFile->zName);
644 }
645 pNew->isExe = pFile->isExe;
646 pNew->isLink = pFile->isLink;
647 pNew->zUuid = fossil_strdup(pFile->zUuid);
648 pNew->isFrom = 0;
649 }
650 }else
651 if( memcmp(zLine, "R ", 2)==0 ){
@@ -657,10 +665,11 @@
665 }else{
666 pNew->zName = fossil_strdup(pFile->zName);
667 }
668 pNew->zPrior = pFile->zName;
669 pNew->isExe = pFile->isExe;
670 pNew->isLink = pFile->isLink;
671 pNew->zUuid = pFile->zUuid;
672 pNew->isFrom = 0;
673 gg.nFile--;
674 *pFile = *pNew;
675 memset(pNew, 0, sizeof(*pNew));
@@ -677,10 +686,13 @@
686 {
687 goto malformed_line;
688 }
689 }
690 gg.xFinish();
691 if( gg.hasLinks ){
692 db_set_int("allow-symlinks", 1, 0);
693 }
694 import_reset(1);
695 return;
696
697 malformed_line:
698 trim_newline(zLine);
699
+14 -6
--- src/info.c
+++ src/info.c
@@ -282,21 +282,22 @@
282282
const char *zName, /* Name of the file that has changed */
283283
const char *zOld, /* blob.uuid before change. NULL for added files */
284284
const char *zNew, /* blob.uuid after change. NULL for deletes */
285285
const char *zOldName, /* Prior name. NULL if no name change. */
286286
int showDiff, /* Show edit diffs if true */
287
- int mperm /* EXE permission for zNew */
287
+ int mperm /* executable or symlink permission for zNew */
288288
){
289289
if( !g.okHistory ){
290290
if( zNew==0 ){
291291
@ <p>Deleted %h(zName)</p>
292292
}else if( zOld==0 ){
293293
@ <p>Added %h(zName)</p>
294294
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
295295
@ <p>Name change from %h(zOldName) to %h(zName)
296296
}else if( fossil_strcmp(zNew, zOld)==0 ){
297
- @ <p>Execute permission %s(mperm?"set":"cleared") for %h(zName)</p>
297
+ @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared")
298
+ @ for %h(zName)</p>
298299
}else{
299300
@ <p>Changes to %h(zName)</p>
300301
}
301302
if( showDiff ){
302303
@ <blockquote><pre>
@@ -312,11 +313,11 @@
312313
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
313314
@ <p>Name change from
314315
@ from <a href="%s(g.zTop)/finfo?name=%T(zOldName)">%h(zOldName)</a>
315316
@ to <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>.
316317
}else{
317
- @ <p>Execute permission %s(mperm?"set":"cleared") for
318
+ @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for
318319
@ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
319320
}
320321
}else if( zOld ){
321322
@ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
322323
@ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
@@ -782,11 +783,11 @@
782783
783784
db_prepare(&q,
784785
"SELECT filename.name, datetime(event.mtime),"
785786
" coalesce(event.ecomment,event.comment),"
786787
" coalesce(event.euser,event.user),"
787
- " b.uuid,"
788
+ " b.uuid, mlink.mperm,"
788789
" coalesce((SELECT value FROM tagxref"
789790
" WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk')"
790791
" FROM mlink, filename, event, blob a, blob b"
791792
" WHERE filename.fnid=mlink.fnid"
792793
" AND event.objid=mlink.mid"
@@ -801,16 +802,23 @@
801802
const char *zName = db_column_text(&q, 0);
802803
const char *zDate = db_column_text(&q, 1);
803804
const char *zCom = db_column_text(&q, 2);
804805
const char *zUser = db_column_text(&q, 3);
805806
const char *zVers = db_column_text(&q, 4);
806
- const char *zBr = db_column_text(&q, 5);
807
+ int mPerm = db_column_int(&q, 5);
808
+ const char *zBr = db_column_text(&q, 6);
807809
if( !prevName || fossil_strcmp(zName, prevName) ) {
808810
if( prevName ) {
809811
@ </ul>
810812
}
811
- @ <li>File
813
+ if( mPerm==PERM_LNK ){
814
+ @ <li>Symbolic link
815
+ }else if( mPerm==PERM_EXE ){
816
+ @ <li>Executable file
817
+ }else{
818
+ @ <li>File
819
+ }
812820
if( g.okHistory ){
813821
@ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
814822
}else{
815823
@ %h(zName)
816824
}
817825
--- src/info.c
+++ src/info.c
@@ -282,21 +282,22 @@
282 const char *zName, /* Name of the file that has changed */
283 const char *zOld, /* blob.uuid before change. NULL for added files */
284 const char *zNew, /* blob.uuid after change. NULL for deletes */
285 const char *zOldName, /* Prior name. NULL if no name change. */
286 int showDiff, /* Show edit diffs if true */
287 int mperm /* EXE permission for zNew */
288 ){
289 if( !g.okHistory ){
290 if( zNew==0 ){
291 @ <p>Deleted %h(zName)</p>
292 }else if( zOld==0 ){
293 @ <p>Added %h(zName)</p>
294 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
295 @ <p>Name change from %h(zOldName) to %h(zName)
296 }else if( fossil_strcmp(zNew, zOld)==0 ){
297 @ <p>Execute permission %s(mperm?"set":"cleared") for %h(zName)</p>
 
298 }else{
299 @ <p>Changes to %h(zName)</p>
300 }
301 if( showDiff ){
302 @ <blockquote><pre>
@@ -312,11 +313,11 @@
312 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
313 @ <p>Name change from
314 @ from <a href="%s(g.zTop)/finfo?name=%T(zOldName)">%h(zOldName)</a>
315 @ to <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>.
316 }else{
317 @ <p>Execute permission %s(mperm?"set":"cleared") for
318 @ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
319 }
320 }else if( zOld ){
321 @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
322 @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
@@ -782,11 +783,11 @@
782
783 db_prepare(&q,
784 "SELECT filename.name, datetime(event.mtime),"
785 " coalesce(event.ecomment,event.comment),"
786 " coalesce(event.euser,event.user),"
787 " b.uuid,"
788 " coalesce((SELECT value FROM tagxref"
789 " WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk')"
790 " FROM mlink, filename, event, blob a, blob b"
791 " WHERE filename.fnid=mlink.fnid"
792 " AND event.objid=mlink.mid"
@@ -801,16 +802,23 @@
801 const char *zName = db_column_text(&q, 0);
802 const char *zDate = db_column_text(&q, 1);
803 const char *zCom = db_column_text(&q, 2);
804 const char *zUser = db_column_text(&q, 3);
805 const char *zVers = db_column_text(&q, 4);
806 const char *zBr = db_column_text(&q, 5);
 
807 if( !prevName || fossil_strcmp(zName, prevName) ) {
808 if( prevName ) {
809 @ </ul>
810 }
811 @ <li>File
 
 
 
 
 
 
812 if( g.okHistory ){
813 @ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
814 }else{
815 @ %h(zName)
816 }
817
--- src/info.c
+++ src/info.c
@@ -282,21 +282,22 @@
282 const char *zName, /* Name of the file that has changed */
283 const char *zOld, /* blob.uuid before change. NULL for added files */
284 const char *zNew, /* blob.uuid after change. NULL for deletes */
285 const char *zOldName, /* Prior name. NULL if no name change. */
286 int showDiff, /* Show edit diffs if true */
287 int mperm /* executable or symlink permission for zNew */
288 ){
289 if( !g.okHistory ){
290 if( zNew==0 ){
291 @ <p>Deleted %h(zName)</p>
292 }else if( zOld==0 ){
293 @ <p>Added %h(zName)</p>
294 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
295 @ <p>Name change from %h(zOldName) to %h(zName)
296 }else if( fossil_strcmp(zNew, zOld)==0 ){
297 @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared")
298 @ for %h(zName)</p>
299 }else{
300 @ <p>Changes to %h(zName)</p>
301 }
302 if( showDiff ){
303 @ <blockquote><pre>
@@ -312,11 +313,11 @@
313 }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
314 @ <p>Name change from
315 @ from <a href="%s(g.zTop)/finfo?name=%T(zOldName)">%h(zOldName)</a>
316 @ to <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>.
317 }else{
318 @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for
319 @ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
320 }
321 }else if( zOld ){
322 @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
323 @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
@@ -782,11 +783,11 @@
783
784 db_prepare(&q,
785 "SELECT filename.name, datetime(event.mtime),"
786 " coalesce(event.ecomment,event.comment),"
787 " coalesce(event.euser,event.user),"
788 " b.uuid, mlink.mperm,"
789 " coalesce((SELECT value FROM tagxref"
790 " WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk')"
791 " FROM mlink, filename, event, blob a, blob b"
792 " WHERE filename.fnid=mlink.fnid"
793 " AND event.objid=mlink.mid"
@@ -801,16 +802,23 @@
802 const char *zName = db_column_text(&q, 0);
803 const char *zDate = db_column_text(&q, 1);
804 const char *zCom = db_column_text(&q, 2);
805 const char *zUser = db_column_text(&q, 3);
806 const char *zVers = db_column_text(&q, 4);
807 int mPerm = db_column_int(&q, 5);
808 const char *zBr = db_column_text(&q, 6);
809 if( !prevName || fossil_strcmp(zName, prevName) ) {
810 if( prevName ) {
811 @ </ul>
812 }
813 if( mPerm==PERM_LNK ){
814 @ <li>Symbolic link
815 }else if( mPerm==PERM_EXE ){
816 @ <li>Executable file
817 }else{
818 @ <li>File
819 }
820 if( g.okHistory ){
821 @ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
822 }else{
823 @ %h(zName)
824 }
825
+2
--- src/main.c
+++ src/main.c
@@ -156,10 +156,12 @@
156156
const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
157157
char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */
158158
const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
159159
const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
160160
int anAuxCols[MX_AUX]; /* Number of columns for option() values */
161
+
162
+ int allowSymlinks; /* Cached "allow-symlinks" option */
161163
};
162164
163165
/*
164166
** Macro for debugging:
165167
*/
166168
--- src/main.c
+++ src/main.c
@@ -156,10 +156,12 @@
156 const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
157 char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */
158 const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
159 const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
160 int anAuxCols[MX_AUX]; /* Number of columns for option() values */
 
 
161 };
162
163 /*
164 ** Macro for debugging:
165 */
166
--- src/main.c
+++ src/main.c
@@ -156,10 +156,12 @@
156 const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
157 char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */
158 const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
159 const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
160 int anAuxCols[MX_AUX]; /* Number of columns for option() values */
161
162 int allowSymlinks; /* Cached "allow-symlinks" option */
163 };
164
165 /*
166 ** Macro for debugging:
167 */
168
+14 -4
--- src/manifest.c
+++ src/manifest.c
@@ -35,10 +35,17 @@
3535
#define CFTYPE_WIKI 4
3636
#define CFTYPE_TICKET 5
3737
#define CFTYPE_ATTACHMENT 6
3838
#define CFTYPE_EVENT 7
3939
40
+/*
41
+** File permissions used by Fossil internally.
42
+*/
43
+#define PERM_REG 0 /* regular file */
44
+#define PERM_EXE 1 /* executable */
45
+#define PERM_LNK 2 /* symlink */
46
+
4047
/*
4148
** A single F-card within a manifest
4249
*/
4350
struct ManifestFile {
4451
char *zName; /* Name of a file */
@@ -1085,13 +1092,16 @@
10851092
/*
10861093
** Compute an appropriate mlink.mperm integer for the permission string
10871094
** of a file.
10881095
*/
10891096
int manifest_file_mperm(ManifestFile *pFile){
1090
- int mperm = 0;
1091
- if( pFile && pFile->zPerm && strstr(pFile->zPerm,"x")!=0 ){
1092
- mperm = 1;
1097
+ int mperm = PERM_REG;
1098
+ if( pFile && pFile->zPerm){
1099
+ if( strstr(pFile->zPerm,"x")!=0 )
1100
+ mperm = PERM_EXE;
1101
+ else if( strstr(pFile->zPerm,"l")!=0 )
1102
+ mperm = PERM_LNK;
10931103
}
10941104
return mperm;
10951105
}
10961106
10971107
/*
@@ -1103,11 +1113,11 @@
11031113
const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */
11041114
const char *zToUuid, /* UUID for the mlink.fid. "" to delele */
11051115
const char *zFilename, /* Filename */
11061116
const char *zPrior, /* Previous filename. NULL if unchanged */
11071117
int isPublic, /* True if mid is not a private manifest */
1108
- int mperm /* 1: exec */
1118
+ int mperm /* 1: exec, 2: symlink */
11091119
){
11101120
int fnid, pfnid, pid, fid;
11111121
static Stmt s1;
11121122
11131123
fnid = filename_to_fnid(zFilename);
11141124
--- src/manifest.c
+++ src/manifest.c
@@ -35,10 +35,17 @@
35 #define CFTYPE_WIKI 4
36 #define CFTYPE_TICKET 5
37 #define CFTYPE_ATTACHMENT 6
38 #define CFTYPE_EVENT 7
39
 
 
 
 
 
 
 
40 /*
41 ** A single F-card within a manifest
42 */
43 struct ManifestFile {
44 char *zName; /* Name of a file */
@@ -1085,13 +1092,16 @@
1085 /*
1086 ** Compute an appropriate mlink.mperm integer for the permission string
1087 ** of a file.
1088 */
1089 int manifest_file_mperm(ManifestFile *pFile){
1090 int mperm = 0;
1091 if( pFile && pFile->zPerm && strstr(pFile->zPerm,"x")!=0 ){
1092 mperm = 1;
 
 
 
1093 }
1094 return mperm;
1095 }
1096
1097 /*
@@ -1103,11 +1113,11 @@
1103 const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */
1104 const char *zToUuid, /* UUID for the mlink.fid. "" to delele */
1105 const char *zFilename, /* Filename */
1106 const char *zPrior, /* Previous filename. NULL if unchanged */
1107 int isPublic, /* True if mid is not a private manifest */
1108 int mperm /* 1: exec */
1109 ){
1110 int fnid, pfnid, pid, fid;
1111 static Stmt s1;
1112
1113 fnid = filename_to_fnid(zFilename);
1114
--- src/manifest.c
+++ src/manifest.c
@@ -35,10 +35,17 @@
35 #define CFTYPE_WIKI 4
36 #define CFTYPE_TICKET 5
37 #define CFTYPE_ATTACHMENT 6
38 #define CFTYPE_EVENT 7
39
40 /*
41 ** File permissions used by Fossil internally.
42 */
43 #define PERM_REG 0 /* regular file */
44 #define PERM_EXE 1 /* executable */
45 #define PERM_LNK 2 /* symlink */
46
47 /*
48 ** A single F-card within a manifest
49 */
50 struct ManifestFile {
51 char *zName; /* Name of a file */
@@ -1085,13 +1092,16 @@
1092 /*
1093 ** Compute an appropriate mlink.mperm integer for the permission string
1094 ** of a file.
1095 */
1096 int manifest_file_mperm(ManifestFile *pFile){
1097 int mperm = PERM_REG;
1098 if( pFile && pFile->zPerm){
1099 if( strstr(pFile->zPerm,"x")!=0 )
1100 mperm = PERM_EXE;
1101 else if( strstr(pFile->zPerm,"l")!=0 )
1102 mperm = PERM_LNK;
1103 }
1104 return mperm;
1105 }
1106
1107 /*
@@ -1103,11 +1113,11 @@
1113 const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */
1114 const char *zToUuid, /* UUID for the mlink.fid. "" to delele */
1115 const char *zFilename, /* Filename */
1116 const char *zPrior, /* Previous filename. NULL if unchanged */
1117 int isPublic, /* True if mid is not a private manifest */
1118 int mperm /* 1: exec, 2: symlink */
1119 ){
1120 int fnid, pfnid, pid, fid;
1121 static Stmt s1;
1122
1123 fnid = filename_to_fnid(zFilename);
1124
+50 -30
--- src/merge.c
+++ src/merge.c
@@ -167,11 +167,13 @@
167167
" ridv INTEGER," /* Record ID for current version */
168168
" ridp INTEGER," /* Record ID for pivot */
169169
" ridm INTEGER," /* Record ID for merge */
170170
" isexe BOOLEAN," /* Execute permission enabled */
171171
" fnp TEXT," /* The filename in the pivot */
172
- " fnm TEXT" /* the filename in the merged version */
172
+ " fnm TEXT," /* the filename in the merged version */
173
+ " islinkv BOOLEAN," /* True if current version is a symlink */
174
+ " islinkm BOOLEAN" /* True if merged version in is a symlink */
173175
");",
174176
caseSensitive ? "binary" : "nocase"
175177
);
176178
177179
/* Add files found in V
@@ -317,11 +319,22 @@
317319
}
318320
}
319321
db_finalize(&q);
320322
321323
/*
322
- ** Find files that have changed from P->M but not P->V.
324
+ ** Add islink information for files in V and M
325
+ **
326
+ */
327
+ db_multi_exec(
328
+ "UPDATE fv SET"
329
+ " islinkv=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnm),0),"
330
+ " islinkm=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnm),0)",
331
+ vid, mid
332
+ );
333
+
334
+ /*
335
+ ** Find files that have changed from P->M but not P->V.
323336
** Copy the M content over into V.
324337
*/
325338
db_prepare(&q,
326339
"SELECT idv, ridm, fn FROM fv"
327340
" WHERE idp>0 AND idv>0 AND idm>0"
@@ -345,11 +358,11 @@
345358
346359
/*
347360
** Do a three-way merge on files that have changes on both P->M and P->V.
348361
*/
349362
db_prepare(&q,
350
- "SELECT ridm, idv, ridp, ridv, %s, fn, isexe FROM fv"
363
+ "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv"
351364
" WHERE idp>0 AND idv>0 AND idm>0"
352365
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
353366
glob_expr("fv.fn", zBinGlob)
354367
);
355368
while( db_step(&q)==SQLITE_ROW ){
@@ -358,10 +371,12 @@
358371
int ridp = db_column_int(&q, 2);
359372
int ridv = db_column_int(&q, 3);
360373
int isBinary = db_column_int(&q, 4);
361374
const char *zName = db_column_text(&q, 5);
362375
int isExe = db_column_int(&q, 6);
376
+ int islinkv = db_column_int(&q, 7);
377
+ int islinkm = db_column_int(&q, 8);
363378
int rc;
364379
char *zFullPath;
365380
Blob m, p, r;
366381
/* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
367382
if( detailFlag ){
@@ -368,37 +383,42 @@
368383
fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n",
369384
zName, ridp, ridm, ridv);
370385
}else{
371386
fossil_print("MERGE %s\n", zName);
372387
}
373
- undo_save(zName);
374
- zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
375
- content_get(ridp, &p);
376
- content_get(ridm, &m);
377
- if( isBinary ){
378
- rc = -1;
379
- blob_zero(&r);
380
- }else{
381
- rc = merge_3way(&p, zFullPath, &m, &r);
382
- }
383
- if( rc>=0 ){
384
- if( !nochangeFlag ){
385
- blob_write_to_file(&r, zFullPath);
386
- file_setexe(zFullPath, isExe);
387
- }
388
- db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
389
- if( rc>0 ){
390
- fossil_print("***** %d merge conflicts in %s\n", rc, zName);
391
- nConflict++;
392
- }
393
- }else{
394
- fossil_print("***** Cannot merge binary file %s\n", zName);
395
- nConflict++;
396
- }
397
- blob_reset(&p);
398
- blob_reset(&m);
399
- blob_reset(&r);
388
+ if( islinkv || islinkm /* || file_islink(zFullPath) */ ){
389
+ fossil_print("***** Cannot merge symlink %s\n", zName);
390
+ nConflict++;
391
+ }else{
392
+ undo_save(zName);
393
+ zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
394
+ content_get(ridp, &p);
395
+ content_get(ridm, &m);
396
+ if( isBinary ){
397
+ rc = -1;
398
+ blob_zero(&r);
399
+ }else{
400
+ rc = merge_3way(&p, zFullPath, &m, &r);
401
+ }
402
+ if( rc>=0 ){
403
+ if( !nochangeFlag ){
404
+ blob_write_to_file(&r, zFullPath);
405
+ file_setexe(zFullPath, isExe);
406
+ }
407
+ db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
408
+ if( rc>0 ){
409
+ fossil_print("***** %d merge conflicts in %s\n", rc, zName);
410
+ nConflict++;
411
+ }
412
+ }else{
413
+ fossil_print("***** Cannot merge binary file %s\n", zName);
414
+ nConflict++;
415
+ }
416
+ blob_reset(&p);
417
+ blob_reset(&m);
418
+ blob_reset(&r);
419
+ }
400420
db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
401421
idv,ridm);
402422
}
403423
db_finalize(&q);
404424
405425
--- src/merge.c
+++ src/merge.c
@@ -167,11 +167,13 @@
167 " ridv INTEGER," /* Record ID for current version */
168 " ridp INTEGER," /* Record ID for pivot */
169 " ridm INTEGER," /* Record ID for merge */
170 " isexe BOOLEAN," /* Execute permission enabled */
171 " fnp TEXT," /* The filename in the pivot */
172 " fnm TEXT" /* the filename in the merged version */
 
 
173 ");",
174 caseSensitive ? "binary" : "nocase"
175 );
176
177 /* Add files found in V
@@ -317,11 +319,22 @@
317 }
318 }
319 db_finalize(&q);
320
321 /*
322 ** Find files that have changed from P->M but not P->V.
 
 
 
 
 
 
 
 
 
 
 
323 ** Copy the M content over into V.
324 */
325 db_prepare(&q,
326 "SELECT idv, ridm, fn FROM fv"
327 " WHERE idp>0 AND idv>0 AND idm>0"
@@ -345,11 +358,11 @@
345
346 /*
347 ** Do a three-way merge on files that have changes on both P->M and P->V.
348 */
349 db_prepare(&q,
350 "SELECT ridm, idv, ridp, ridv, %s, fn, isexe FROM fv"
351 " WHERE idp>0 AND idv>0 AND idm>0"
352 " AND ridm!=ridp AND (ridv!=ridp OR chnged)",
353 glob_expr("fv.fn", zBinGlob)
354 );
355 while( db_step(&q)==SQLITE_ROW ){
@@ -358,10 +371,12 @@
358 int ridp = db_column_int(&q, 2);
359 int ridv = db_column_int(&q, 3);
360 int isBinary = db_column_int(&q, 4);
361 const char *zName = db_column_text(&q, 5);
362 int isExe = db_column_int(&q, 6);
 
 
363 int rc;
364 char *zFullPath;
365 Blob m, p, r;
366 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
367 if( detailFlag ){
@@ -368,37 +383,42 @@
368 fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n",
369 zName, ridp, ridm, ridv);
370 }else{
371 fossil_print("MERGE %s\n", zName);
372 }
373 undo_save(zName);
374 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
375 content_get(ridp, &p);
376 content_get(ridm, &m);
377 if( isBinary ){
378 rc = -1;
379 blob_zero(&r);
380 }else{
381 rc = merge_3way(&p, zFullPath, &m, &r);
382 }
383 if( rc>=0 ){
384 if( !nochangeFlag ){
385 blob_write_to_file(&r, zFullPath);
386 file_setexe(zFullPath, isExe);
387 }
388 db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
389 if( rc>0 ){
390 fossil_print("***** %d merge conflicts in %s\n", rc, zName);
391 nConflict++;
392 }
393 }else{
394 fossil_print("***** Cannot merge binary file %s\n", zName);
395 nConflict++;
396 }
397 blob_reset(&p);
398 blob_reset(&m);
399 blob_reset(&r);
 
 
 
 
 
400 db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
401 idv,ridm);
402 }
403 db_finalize(&q);
404
405
--- src/merge.c
+++ src/merge.c
@@ -167,11 +167,13 @@
167 " ridv INTEGER," /* Record ID for current version */
168 " ridp INTEGER," /* Record ID for pivot */
169 " ridm INTEGER," /* Record ID for merge */
170 " isexe BOOLEAN," /* Execute permission enabled */
171 " fnp TEXT," /* The filename in the pivot */
172 " fnm TEXT," /* the filename in the merged version */
173 " islinkv BOOLEAN," /* True if current version is a symlink */
174 " islinkm BOOLEAN" /* True if merged version in is a symlink */
175 ");",
176 caseSensitive ? "binary" : "nocase"
177 );
178
179 /* Add files found in V
@@ -317,11 +319,22 @@
319 }
320 }
321 db_finalize(&q);
322
323 /*
324 ** Add islink information for files in V and M
325 **
326 */
327 db_multi_exec(
328 "UPDATE fv SET"
329 " islinkv=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnm),0),"
330 " islinkm=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnm),0)",
331 vid, mid
332 );
333
334 /*
335 ** Find files that have changed from P->M but not P->V.
336 ** Copy the M content over into V.
337 */
338 db_prepare(&q,
339 "SELECT idv, ridm, fn FROM fv"
340 " WHERE idp>0 AND idv>0 AND idm>0"
@@ -345,11 +358,11 @@
358
359 /*
360 ** Do a three-way merge on files that have changes on both P->M and P->V.
361 */
362 db_prepare(&q,
363 "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv"
364 " WHERE idp>0 AND idv>0 AND idm>0"
365 " AND ridm!=ridp AND (ridv!=ridp OR chnged)",
366 glob_expr("fv.fn", zBinGlob)
367 );
368 while( db_step(&q)==SQLITE_ROW ){
@@ -358,10 +371,12 @@
371 int ridp = db_column_int(&q, 2);
372 int ridv = db_column_int(&q, 3);
373 int isBinary = db_column_int(&q, 4);
374 const char *zName = db_column_text(&q, 5);
375 int isExe = db_column_int(&q, 6);
376 int islinkv = db_column_int(&q, 7);
377 int islinkm = db_column_int(&q, 8);
378 int rc;
379 char *zFullPath;
380 Blob m, p, r;
381 /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
382 if( detailFlag ){
@@ -368,37 +383,42 @@
383 fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n",
384 zName, ridp, ridm, ridv);
385 }else{
386 fossil_print("MERGE %s\n", zName);
387 }
388 if( islinkv || islinkm /* || file_islink(zFullPath) */ ){
389 fossil_print("***** Cannot merge symlink %s\n", zName);
390 nConflict++;
391 }else{
392 undo_save(zName);
393 zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
394 content_get(ridp, &p);
395 content_get(ridm, &m);
396 if( isBinary ){
397 rc = -1;
398 blob_zero(&r);
399 }else{
400 rc = merge_3way(&p, zFullPath, &m, &r);
401 }
402 if( rc>=0 ){
403 if( !nochangeFlag ){
404 blob_write_to_file(&r, zFullPath);
405 file_setexe(zFullPath, isExe);
406 }
407 db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
408 if( rc>0 ){
409 fossil_print("***** %d merge conflicts in %s\n", rc, zName);
410 nConflict++;
411 }
412 }else{
413 fossil_print("***** Cannot merge binary file %s\n", zName);
414 nConflict++;
415 }
416 blob_reset(&p);
417 blob_reset(&m);
418 blob_reset(&r);
419 }
420 db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)",
421 idv,ridm);
422 }
423 db_finalize(&q);
424
425
--- src/schema.c
+++ src/schema.c
@@ -461,10 +461,11 @@
461461
@ id INTEGER PRIMARY KEY, -- ID of the checked out file
462462
@ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
463463
@ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add
464464
@ deleted BOOLEAN DEFAULT 0, -- True if deleted
465465
@ isexe BOOLEAN, -- True if file should be executable
466
+@ islink BOOLEAN, -- True if file should be symlink
466467
@ rid INTEGER, -- Originally from this repository record
467468
@ mrid INTEGER, -- Based on this record due to a merge
468469
@ mtime INTEGER, -- Mtime of file on disk. sec since 1970
469470
@ pathname TEXT, -- Full pathname relative to root
470471
@ origname TEXT, -- Original pathname. NULL if unchanged
471472
--- src/schema.c
+++ src/schema.c
@@ -461,10 +461,11 @@
461 @ id INTEGER PRIMARY KEY, -- ID of the checked out file
462 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
463 @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add
464 @ deleted BOOLEAN DEFAULT 0, -- True if deleted
465 @ isexe BOOLEAN, -- True if file should be executable
 
466 @ rid INTEGER, -- Originally from this repository record
467 @ mrid INTEGER, -- Based on this record due to a merge
468 @ mtime INTEGER, -- Mtime of file on disk. sec since 1970
469 @ pathname TEXT, -- Full pathname relative to root
470 @ origname TEXT, -- Original pathname. NULL if unchanged
471
--- src/schema.c
+++ src/schema.c
@@ -461,10 +461,11 @@
461 @ id INTEGER PRIMARY KEY, -- ID of the checked out file
462 @ vid INTEGER REFERENCES blob, -- The baseline this file is part of.
463 @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add
464 @ deleted BOOLEAN DEFAULT 0, -- True if deleted
465 @ isexe BOOLEAN, -- True if file should be executable
466 @ islink BOOLEAN, -- True if file should be symlink
467 @ rid INTEGER, -- Originally from this repository record
468 @ mrid INTEGER, -- Based on this record due to a merge
469 @ mtime INTEGER, -- Mtime of file on disk. sec since 1970
470 @ pathname TEXT, -- Full pathname relative to root
471 @ origname TEXT, -- Original pathname. NULL if unchanged
472
+11
--- src/sha1.c
+++ src/sha1.c
@@ -280,10 +280,21 @@
280280
int sha1sum_file(const char *zFilename, Blob *pCksum){
281281
FILE *in;
282282
SHA1Context ctx;
283283
unsigned char zResult[20];
284284
char zBuf[10240];
285
+
286
+ if( file_islink(zFilename) ){
287
+ /* Instead of file content, return sha1 of link destination path */
288
+ Blob destinationPath;
289
+ int rc;
290
+
291
+ blob_read_link(&destinationPath, zFilename);
292
+ rc = sha1sum_blob(&destinationPath, pCksum);
293
+ blob_reset(&destinationPath);
294
+ return rc;
295
+ }
285296
286297
in = fossil_fopen(zFilename,"rb");
287298
if( in==0 ){
288299
return 1;
289300
}
290301
--- src/sha1.c
+++ src/sha1.c
@@ -280,10 +280,21 @@
280 int sha1sum_file(const char *zFilename, Blob *pCksum){
281 FILE *in;
282 SHA1Context ctx;
283 unsigned char zResult[20];
284 char zBuf[10240];
 
 
 
 
 
 
 
 
 
 
 
285
286 in = fossil_fopen(zFilename,"rb");
287 if( in==0 ){
288 return 1;
289 }
290
--- src/sha1.c
+++ src/sha1.c
@@ -280,10 +280,21 @@
280 int sha1sum_file(const char *zFilename, Blob *pCksum){
281 FILE *in;
282 SHA1Context ctx;
283 unsigned char zResult[20];
284 char zBuf[10240];
285
286 if( file_islink(zFilename) ){
287 /* Instead of file content, return sha1 of link destination path */
288 Blob destinationPath;
289 int rc;
290
291 blob_read_link(&destinationPath, zFilename);
292 rc = sha1sum_blob(&destinationPath, pCksum);
293 blob_reset(&destinationPath);
294 return rc;
295 }
296
297 in = fossil_fopen(zFilename,"rb");
298 if( in==0 ){
299 return 1;
300 }
301
+84 -33
--- src/stash.c
+++ src/stash.c
@@ -35,10 +35,11 @@
3535
@ stashid INTEGER REFERENCES stash, -- Stash that contains this file
3636
@ rid INTEGER, -- Baseline content in BLOB table or 0.
3737
@ isAdded BOOLEAN, -- True if this is an added file
3838
@ isRemoved BOOLEAN, -- True if this file is deleted
3939
@ isExec BOOLEAN, -- True if file is executable
40
+@ isLink BOOLEAN, -- True if file is a symlink
4041
@ origname TEXT, -- Original filename
4142
@ newname TEXT, -- New name for file at next check-in
4243
@ delta BLOB, -- Delta from baseline. Content if rid=0
4344
@ PRIMARY KEY(origname, stashid)
4445
@ );
@@ -61,11 +62,11 @@
6162
zFile = mprintf("%/", zFName);
6263
file_tree_name(zFile, &fname, 1);
6364
zTreename = blob_str(&fname);
6465
blob_zero(&sql);
6566
blob_appendf(&sql,
66
- "SELECT deleted, isexe, mrid, pathname, coalesce(origname,pathname)"
67
+ "SELECT deleted, isexe, islink, mrid, pathname, coalesce(origname,pathname)"
6768
" FROM vfile"
6869
" WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)",
6970
vid
7071
);
7172
if( fossil_strcmp(zTreename,".")!=0 ){
@@ -76,47 +77,60 @@
7677
);
7778
}
7879
db_prepare(&q, blob_str(&sql));
7980
blob_reset(&sql);
8081
db_prepare(&ins,
81
- "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec,"
82
+ "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
8283
"origname, newname, delta)"
83
- "VALUES(%d,:rid,:isadd,:isrm,:isexe,:orig,:new,:content)",
84
+ "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
8485
stashid
8586
);
8687
while( db_step(&q)==SQLITE_ROW ){
8788
int deleted = db_column_int(&q, 0);
88
- int rid = db_column_int(&q, 2);
89
- const char *zName = db_column_text(&q, 3);
90
- const char *zOrig = db_column_text(&q, 4);
89
+ int rid = db_column_int(&q, 3);
90
+ const char *zName = db_column_text(&q, 4);
91
+ const char *zOrig = db_column_text(&q, 5);
9192
char *zPath = mprintf("%s%s", g.zLocalRoot, zName);
9293
Blob content;
94
+ int isNewLink = file_islink(zPath);
9395
9496
db_bind_int(&ins, ":rid", rid);
9597
db_bind_int(&ins, ":isadd", rid==0);
9698
db_bind_int(&ins, ":isrm", deleted);
9799
db_bind_int(&ins, ":isexe", db_column_int(&q, 1));
100
+ db_bind_int(&ins, ":islink", db_column_int(&q, 2));
98101
db_bind_text(&ins, ":orig", zOrig);
99102
db_bind_text(&ins, ":new", zName);
103
+
100104
if( rid==0 ){
101105
/* A new file */
102
- blob_read_from_file(&content, zPath);
106
+ if( isNewLink ){
107
+ blob_read_link(&content, zPath);
108
+ }else{
109
+ blob_read_from_file(&content, zPath);
110
+ }
103111
db_bind_blob(&ins, ":content", &content);
104112
}else if( deleted ){
105113
blob_zero(&content);
106114
db_bind_null(&ins, ":content");
107115
}else{
108116
/* A modified file */
109117
Blob orig;
110118
Blob disk;
111
- blob_read_from_file(&disk, zPath);
119
+
120
+ if( isNewLink ){
121
+ blob_read_link(&disk, zPath);
122
+ }else{
123
+ blob_read_from_file(&disk, zPath);
124
+ }
112125
content_get(rid, &orig);
113126
blob_delta_create(&orig, &disk, &content);
114127
blob_reset(&orig);
115128
blob_reset(&disk);
116129
db_bind_blob(&ins, ":content", &content);
117130
}
131
+ db_bind_int(&ins, ":islink", isNewLink);
118132
db_step(&ins);
119133
db_reset(&ins);
120134
fossil_free(zPath);
121135
blob_reset(&content);
122136
}
@@ -167,54 +181,74 @@
167181
** Apply a stash to the current check-out.
168182
*/
169183
static void stash_apply(int stashid, int nConflict){
170184
Stmt q;
171185
db_prepare(&q,
172
- "SELECT rid, isRemoved, isExec, origname, newname, delta"
186
+ "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
173187
" FROM stashfile WHERE stashid=%d",
174188
stashid
175189
);
176190
while( db_step(&q)==SQLITE_ROW ){
177191
int rid = db_column_int(&q, 0);
178192
int isRemoved = db_column_int(&q, 1);
179193
int isExec = db_column_int(&q, 2);
180
- const char *zOrig = db_column_text(&q, 3);
181
- const char *zNew = db_column_text(&q, 4);
194
+ int isLink = db_column_int(&q, 3);
195
+ const char *zOrig = db_column_text(&q, 4);
196
+ const char *zNew = db_column_text(&q, 5);
182197
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
183198
char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew);
184199
Blob delta;
185200
undo_save(zNew);
186201
blob_zero(&delta);
187202
if( rid==0 ){
188
- db_ephemeral_blob(&q, 5, &delta);
203
+ db_ephemeral_blob(&q, 6, &delta);
189204
blob_write_to_file(&delta, zNPath);
190205
file_setexe(zNPath, isExec);
191206
fossil_print("ADD %s\n", zNew);
192207
}else if( isRemoved ){
193208
fossil_print("DELETE %s\n", zOrig);
194209
file_delete(zOPath);
195210
}else{
196211
Blob a, b, out, disk;
197
- db_ephemeral_blob(&q, 5, &delta);
198
- blob_read_from_file(&disk, zOPath);
212
+ int isNewLink = file_islink(zOPath);
213
+ db_ephemeral_blob(&q, 6, &delta);
214
+ if( isNewLink ){
215
+ blob_read_link(&disk, zOPath);
216
+ }else{
217
+ blob_read_from_file(&disk, zOPath);
218
+ }
199219
content_get(rid, &a);
200220
blob_delta_apply(&a, &delta, &b);
201
- if( blob_compare(&disk, &a)==0 ){
202
- blob_write_to_file(&b, zNPath);
221
+ if( blob_compare(&disk, &a)==0 && isLink == isNewLink ){
222
+ if( isLink || isNewLink ){
223
+ unlink(zNPath);
224
+ }
225
+ if( isLink ){
226
+ create_symlink(blob_str(&b), zNPath);
227
+ }else{
228
+ blob_write_to_file(&b, zNPath);
229
+ }
203230
file_setexe(zNPath, isExec);
204231
fossil_print("UPDATE %s\n", zNew);
205232
}else{
206
- int rc = merge_3way(&a, zOPath, &b, &out);
207
- blob_write_to_file(&out, zNPath);
233
+ int rc;
234
+ if( isLink || isNewLink ){
235
+ rc = -1;
236
+ blob_zero(&b); /* because we reset it later */
237
+ //TODO(dchest): write something to disk?
238
+ }else{
239
+ rc = merge_3way(&a, zOPath, &b, &out);
240
+ blob_write_to_file(&out, zNPath);
241
+ //blob_reset(&out); //XXX(dchest): Need this?
242
+ }
208243
file_setexe(zNPath, isExec);
209244
if( rc ){
210245
fossil_print("CONFLICT %s\n", zNew);
211246
nConflict++;
212247
}else{
213248
fossil_print("MERGE %s\n", zNew);
214249
}
215
- blob_reset(&out);
216250
}
217251
blob_reset(&a);
218252
blob_reset(&b);
219253
blob_reset(&disk);
220254
}
@@ -224,11 +258,12 @@
224258
file_delete(zOPath);
225259
}
226260
}
227261
db_finalize(&q);
228262
if( nConflict ){
229
- fossil_print("WARNING: merge conflicts - see messages above for details.\n");
263
+ fossil_print("WARNING: %d merge conflicts - see messages above for details.\n",
264
+ nConflict);
230265
}
231266
}
232267
233268
/*
234269
** Show the diffs associate with a single stash.
@@ -236,41 +271,57 @@
236271
static void stash_diff(int stashid, const char *zDiffCmd){
237272
Stmt q;
238273
Blob empty;
239274
blob_zero(&empty);
240275
db_prepare(&q,
241
- "SELECT rid, isRemoved, isExec, origname, newname, delta"
276
+ "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
242277
" FROM stashfile WHERE stashid=%d",
243278
stashid
244279
);
245280
while( db_step(&q)==SQLITE_ROW ){
246281
int rid = db_column_int(&q, 0);
247282
int isRemoved = db_column_int(&q, 1);
248
- const char *zOrig = db_column_text(&q, 3);
249
- const char *zNew = db_column_text(&q, 4);
283
+ int isLink = db_column_int(&q, 3);
284
+ const char *zOrig = db_column_text(&q, 4);
285
+ const char *zNew = db_column_text(&q, 5);
250286
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
251287
Blob delta;
252288
if( rid==0 ){
253
- db_ephemeral_blob(&q, 5, &delta);
289
+ db_ephemeral_blob(&q, 6, &delta);
254290
fossil_print("ADDED %s\n", zNew);
255291
diff_print_index(zNew);
256292
diff_file_mem(&empty, &delta, zNew, zDiffCmd, 0);
257293
}else if( isRemoved ){
258294
fossil_print("DELETE %s\n", zOrig);
259
- blob_read_from_file(&delta, zOPath);
295
+ if( file_islink(zOPath) ){
296
+ blob_read_link(&delta, zOPath);
297
+ }else{
298
+ blob_read_from_file(&delta, zOPath);
299
+ }
260300
diff_print_index(zNew);
261301
diff_file_mem(&delta, &empty, zOrig, zDiffCmd, 0);
262302
}else{
263303
Blob a, b, disk;
264
- db_ephemeral_blob(&q, 5, &delta);
265
- blob_read_from_file(&disk, zOPath);
266
- content_get(rid, &a);
267
- blob_delta_apply(&a, &delta, &b);
304
+ int isOrigLink = file_islink(zOPath);
305
+ db_ephemeral_blob(&q, 6, &delta);
306
+ if( isOrigLink ){
307
+ blob_read_link(&disk, zOPath);
308
+ }else{
309
+ blob_read_from_file(&disk, zOPath);
310
+ }
268311
fossil_print("CHANGED %s\n", zNew);
269
- diff_file_mem(&disk, &b, zNew, zDiffCmd, 0);
270
- blob_reset(&a);
271
- blob_reset(&b);
312
+ if( !isOrigLink != !isLink ){
313
+ diff_print_index(zNew);
314
+ printf("--- %s\n+++ %s\n", zOrig, zNew);
315
+ printf("cannot compute difference between symlink and regular file\n");
316
+ }else{
317
+ content_get(rid, &a);
318
+ blob_delta_apply(&a, &delta, &b);
319
+ diff_file_mem(&disk, &b, zNew, zDiffCmd, 0);
320
+ blob_reset(&a);
321
+ blob_reset(&b);
322
+ }
272323
blob_reset(&disk);
273324
}
274325
blob_reset(&delta);
275326
}
276327
db_finalize(&q);
@@ -352,11 +403,11 @@
352403
** Forget everything about STASHID. Forget the whole stash if the
353404
** --all flag is used. Individual drops are undoable but --all is not.
354405
**
355406
** fossil stash snapshot ?-m COMMENT? ?FILES...?
356407
**
357
-** Save the current changes in the working tress as a new stash
408
+** Save the current changes in the working tree as a new stash
358409
** but, unlike "save", do not revert those changes.
359410
**
360411
** fossil stash diff ?STASHID?
361412
** fossil stash gdiff ?STASHID?
362413
**
363414
--- src/stash.c
+++ src/stash.c
@@ -35,10 +35,11 @@
35 @ stashid INTEGER REFERENCES stash, -- Stash that contains this file
36 @ rid INTEGER, -- Baseline content in BLOB table or 0.
37 @ isAdded BOOLEAN, -- True if this is an added file
38 @ isRemoved BOOLEAN, -- True if this file is deleted
39 @ isExec BOOLEAN, -- True if file is executable
 
40 @ origname TEXT, -- Original filename
41 @ newname TEXT, -- New name for file at next check-in
42 @ delta BLOB, -- Delta from baseline. Content if rid=0
43 @ PRIMARY KEY(origname, stashid)
44 @ );
@@ -61,11 +62,11 @@
61 zFile = mprintf("%/", zFName);
62 file_tree_name(zFile, &fname, 1);
63 zTreename = blob_str(&fname);
64 blob_zero(&sql);
65 blob_appendf(&sql,
66 "SELECT deleted, isexe, mrid, pathname, coalesce(origname,pathname)"
67 " FROM vfile"
68 " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)",
69 vid
70 );
71 if( fossil_strcmp(zTreename,".")!=0 ){
@@ -76,47 +77,60 @@
76 );
77 }
78 db_prepare(&q, blob_str(&sql));
79 blob_reset(&sql);
80 db_prepare(&ins,
81 "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec,"
82 "origname, newname, delta)"
83 "VALUES(%d,:rid,:isadd,:isrm,:isexe,:orig,:new,:content)",
84 stashid
85 );
86 while( db_step(&q)==SQLITE_ROW ){
87 int deleted = db_column_int(&q, 0);
88 int rid = db_column_int(&q, 2);
89 const char *zName = db_column_text(&q, 3);
90 const char *zOrig = db_column_text(&q, 4);
91 char *zPath = mprintf("%s%s", g.zLocalRoot, zName);
92 Blob content;
 
93
94 db_bind_int(&ins, ":rid", rid);
95 db_bind_int(&ins, ":isadd", rid==0);
96 db_bind_int(&ins, ":isrm", deleted);
97 db_bind_int(&ins, ":isexe", db_column_int(&q, 1));
 
98 db_bind_text(&ins, ":orig", zOrig);
99 db_bind_text(&ins, ":new", zName);
 
100 if( rid==0 ){
101 /* A new file */
102 blob_read_from_file(&content, zPath);
 
 
 
 
103 db_bind_blob(&ins, ":content", &content);
104 }else if( deleted ){
105 blob_zero(&content);
106 db_bind_null(&ins, ":content");
107 }else{
108 /* A modified file */
109 Blob orig;
110 Blob disk;
111 blob_read_from_file(&disk, zPath);
 
 
 
 
 
112 content_get(rid, &orig);
113 blob_delta_create(&orig, &disk, &content);
114 blob_reset(&orig);
115 blob_reset(&disk);
116 db_bind_blob(&ins, ":content", &content);
117 }
 
118 db_step(&ins);
119 db_reset(&ins);
120 fossil_free(zPath);
121 blob_reset(&content);
122 }
@@ -167,54 +181,74 @@
167 ** Apply a stash to the current check-out.
168 */
169 static void stash_apply(int stashid, int nConflict){
170 Stmt q;
171 db_prepare(&q,
172 "SELECT rid, isRemoved, isExec, origname, newname, delta"
173 " FROM stashfile WHERE stashid=%d",
174 stashid
175 );
176 while( db_step(&q)==SQLITE_ROW ){
177 int rid = db_column_int(&q, 0);
178 int isRemoved = db_column_int(&q, 1);
179 int isExec = db_column_int(&q, 2);
180 const char *zOrig = db_column_text(&q, 3);
181 const char *zNew = db_column_text(&q, 4);
 
182 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
183 char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew);
184 Blob delta;
185 undo_save(zNew);
186 blob_zero(&delta);
187 if( rid==0 ){
188 db_ephemeral_blob(&q, 5, &delta);
189 blob_write_to_file(&delta, zNPath);
190 file_setexe(zNPath, isExec);
191 fossil_print("ADD %s\n", zNew);
192 }else if( isRemoved ){
193 fossil_print("DELETE %s\n", zOrig);
194 file_delete(zOPath);
195 }else{
196 Blob a, b, out, disk;
197 db_ephemeral_blob(&q, 5, &delta);
198 blob_read_from_file(&disk, zOPath);
 
 
 
 
 
199 content_get(rid, &a);
200 blob_delta_apply(&a, &delta, &b);
201 if( blob_compare(&disk, &a)==0 ){
202 blob_write_to_file(&b, zNPath);
 
 
 
 
 
 
 
203 file_setexe(zNPath, isExec);
204 fossil_print("UPDATE %s\n", zNew);
205 }else{
206 int rc = merge_3way(&a, zOPath, &b, &out);
207 blob_write_to_file(&out, zNPath);
 
 
 
 
 
 
 
 
208 file_setexe(zNPath, isExec);
209 if( rc ){
210 fossil_print("CONFLICT %s\n", zNew);
211 nConflict++;
212 }else{
213 fossil_print("MERGE %s\n", zNew);
214 }
215 blob_reset(&out);
216 }
217 blob_reset(&a);
218 blob_reset(&b);
219 blob_reset(&disk);
220 }
@@ -224,11 +258,12 @@
224 file_delete(zOPath);
225 }
226 }
227 db_finalize(&q);
228 if( nConflict ){
229 fossil_print("WARNING: merge conflicts - see messages above for details.\n");
 
230 }
231 }
232
233 /*
234 ** Show the diffs associate with a single stash.
@@ -236,41 +271,57 @@
236 static void stash_diff(int stashid, const char *zDiffCmd){
237 Stmt q;
238 Blob empty;
239 blob_zero(&empty);
240 db_prepare(&q,
241 "SELECT rid, isRemoved, isExec, origname, newname, delta"
242 " FROM stashfile WHERE stashid=%d",
243 stashid
244 );
245 while( db_step(&q)==SQLITE_ROW ){
246 int rid = db_column_int(&q, 0);
247 int isRemoved = db_column_int(&q, 1);
248 const char *zOrig = db_column_text(&q, 3);
249 const char *zNew = db_column_text(&q, 4);
 
250 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
251 Blob delta;
252 if( rid==0 ){
253 db_ephemeral_blob(&q, 5, &delta);
254 fossil_print("ADDED %s\n", zNew);
255 diff_print_index(zNew);
256 diff_file_mem(&empty, &delta, zNew, zDiffCmd, 0);
257 }else if( isRemoved ){
258 fossil_print("DELETE %s\n", zOrig);
259 blob_read_from_file(&delta, zOPath);
 
 
 
 
260 diff_print_index(zNew);
261 diff_file_mem(&delta, &empty, zOrig, zDiffCmd, 0);
262 }else{
263 Blob a, b, disk;
264 db_ephemeral_blob(&q, 5, &delta);
265 blob_read_from_file(&disk, zOPath);
266 content_get(rid, &a);
267 blob_delta_apply(&a, &delta, &b);
 
 
 
268 fossil_print("CHANGED %s\n", zNew);
269 diff_file_mem(&disk, &b, zNew, zDiffCmd, 0);
270 blob_reset(&a);
271 blob_reset(&b);
 
 
 
 
 
 
 
 
272 blob_reset(&disk);
273 }
274 blob_reset(&delta);
275 }
276 db_finalize(&q);
@@ -352,11 +403,11 @@
352 ** Forget everything about STASHID. Forget the whole stash if the
353 ** --all flag is used. Individual drops are undoable but --all is not.
354 **
355 ** fossil stash snapshot ?-m COMMENT? ?FILES...?
356 **
357 ** Save the current changes in the working tress as a new stash
358 ** but, unlike "save", do not revert those changes.
359 **
360 ** fossil stash diff ?STASHID?
361 ** fossil stash gdiff ?STASHID?
362 **
363
--- src/stash.c
+++ src/stash.c
@@ -35,10 +35,11 @@
35 @ stashid INTEGER REFERENCES stash, -- Stash that contains this file
36 @ rid INTEGER, -- Baseline content in BLOB table or 0.
37 @ isAdded BOOLEAN, -- True if this is an added file
38 @ isRemoved BOOLEAN, -- True if this file is deleted
39 @ isExec BOOLEAN, -- True if file is executable
40 @ isLink BOOLEAN, -- True if file is a symlink
41 @ origname TEXT, -- Original filename
42 @ newname TEXT, -- New name for file at next check-in
43 @ delta BLOB, -- Delta from baseline. Content if rid=0
44 @ PRIMARY KEY(origname, stashid)
45 @ );
@@ -61,11 +62,11 @@
62 zFile = mprintf("%/", zFName);
63 file_tree_name(zFile, &fname, 1);
64 zTreename = blob_str(&fname);
65 blob_zero(&sql);
66 blob_appendf(&sql,
67 "SELECT deleted, isexe, islink, mrid, pathname, coalesce(origname,pathname)"
68 " FROM vfile"
69 " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)",
70 vid
71 );
72 if( fossil_strcmp(zTreename,".")!=0 ){
@@ -76,47 +77,60 @@
77 );
78 }
79 db_prepare(&q, blob_str(&sql));
80 blob_reset(&sql);
81 db_prepare(&ins,
82 "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink,"
83 "origname, newname, delta)"
84 "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)",
85 stashid
86 );
87 while( db_step(&q)==SQLITE_ROW ){
88 int deleted = db_column_int(&q, 0);
89 int rid = db_column_int(&q, 3);
90 const char *zName = db_column_text(&q, 4);
91 const char *zOrig = db_column_text(&q, 5);
92 char *zPath = mprintf("%s%s", g.zLocalRoot, zName);
93 Blob content;
94 int isNewLink = file_islink(zPath);
95
96 db_bind_int(&ins, ":rid", rid);
97 db_bind_int(&ins, ":isadd", rid==0);
98 db_bind_int(&ins, ":isrm", deleted);
99 db_bind_int(&ins, ":isexe", db_column_int(&q, 1));
100 db_bind_int(&ins, ":islink", db_column_int(&q, 2));
101 db_bind_text(&ins, ":orig", zOrig);
102 db_bind_text(&ins, ":new", zName);
103
104 if( rid==0 ){
105 /* A new file */
106 if( isNewLink ){
107 blob_read_link(&content, zPath);
108 }else{
109 blob_read_from_file(&content, zPath);
110 }
111 db_bind_blob(&ins, ":content", &content);
112 }else if( deleted ){
113 blob_zero(&content);
114 db_bind_null(&ins, ":content");
115 }else{
116 /* A modified file */
117 Blob orig;
118 Blob disk;
119
120 if( isNewLink ){
121 blob_read_link(&disk, zPath);
122 }else{
123 blob_read_from_file(&disk, zPath);
124 }
125 content_get(rid, &orig);
126 blob_delta_create(&orig, &disk, &content);
127 blob_reset(&orig);
128 blob_reset(&disk);
129 db_bind_blob(&ins, ":content", &content);
130 }
131 db_bind_int(&ins, ":islink", isNewLink);
132 db_step(&ins);
133 db_reset(&ins);
134 fossil_free(zPath);
135 blob_reset(&content);
136 }
@@ -167,54 +181,74 @@
181 ** Apply a stash to the current check-out.
182 */
183 static void stash_apply(int stashid, int nConflict){
184 Stmt q;
185 db_prepare(&q,
186 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
187 " FROM stashfile WHERE stashid=%d",
188 stashid
189 );
190 while( db_step(&q)==SQLITE_ROW ){
191 int rid = db_column_int(&q, 0);
192 int isRemoved = db_column_int(&q, 1);
193 int isExec = db_column_int(&q, 2);
194 int isLink = db_column_int(&q, 3);
195 const char *zOrig = db_column_text(&q, 4);
196 const char *zNew = db_column_text(&q, 5);
197 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
198 char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew);
199 Blob delta;
200 undo_save(zNew);
201 blob_zero(&delta);
202 if( rid==0 ){
203 db_ephemeral_blob(&q, 6, &delta);
204 blob_write_to_file(&delta, zNPath);
205 file_setexe(zNPath, isExec);
206 fossil_print("ADD %s\n", zNew);
207 }else if( isRemoved ){
208 fossil_print("DELETE %s\n", zOrig);
209 file_delete(zOPath);
210 }else{
211 Blob a, b, out, disk;
212 int isNewLink = file_islink(zOPath);
213 db_ephemeral_blob(&q, 6, &delta);
214 if( isNewLink ){
215 blob_read_link(&disk, zOPath);
216 }else{
217 blob_read_from_file(&disk, zOPath);
218 }
219 content_get(rid, &a);
220 blob_delta_apply(&a, &delta, &b);
221 if( blob_compare(&disk, &a)==0 && isLink == isNewLink ){
222 if( isLink || isNewLink ){
223 unlink(zNPath);
224 }
225 if( isLink ){
226 create_symlink(blob_str(&b), zNPath);
227 }else{
228 blob_write_to_file(&b, zNPath);
229 }
230 file_setexe(zNPath, isExec);
231 fossil_print("UPDATE %s\n", zNew);
232 }else{
233 int rc;
234 if( isLink || isNewLink ){
235 rc = -1;
236 blob_zero(&b); /* because we reset it later */
237 //TODO(dchest): write something to disk?
238 }else{
239 rc = merge_3way(&a, zOPath, &b, &out);
240 blob_write_to_file(&out, zNPath);
241 //blob_reset(&out); //XXX(dchest): Need this?
242 }
243 file_setexe(zNPath, isExec);
244 if( rc ){
245 fossil_print("CONFLICT %s\n", zNew);
246 nConflict++;
247 }else{
248 fossil_print("MERGE %s\n", zNew);
249 }
 
250 }
251 blob_reset(&a);
252 blob_reset(&b);
253 blob_reset(&disk);
254 }
@@ -224,11 +258,12 @@
258 file_delete(zOPath);
259 }
260 }
261 db_finalize(&q);
262 if( nConflict ){
263 fossil_print("WARNING: %d merge conflicts - see messages above for details.\n",
264 nConflict);
265 }
266 }
267
268 /*
269 ** Show the diffs associate with a single stash.
@@ -236,41 +271,57 @@
271 static void stash_diff(int stashid, const char *zDiffCmd){
272 Stmt q;
273 Blob empty;
274 blob_zero(&empty);
275 db_prepare(&q,
276 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
277 " FROM stashfile WHERE stashid=%d",
278 stashid
279 );
280 while( db_step(&q)==SQLITE_ROW ){
281 int rid = db_column_int(&q, 0);
282 int isRemoved = db_column_int(&q, 1);
283 int isLink = db_column_int(&q, 3);
284 const char *zOrig = db_column_text(&q, 4);
285 const char *zNew = db_column_text(&q, 5);
286 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
287 Blob delta;
288 if( rid==0 ){
289 db_ephemeral_blob(&q, 6, &delta);
290 fossil_print("ADDED %s\n", zNew);
291 diff_print_index(zNew);
292 diff_file_mem(&empty, &delta, zNew, zDiffCmd, 0);
293 }else if( isRemoved ){
294 fossil_print("DELETE %s\n", zOrig);
295 if( file_islink(zOPath) ){
296 blob_read_link(&delta, zOPath);
297 }else{
298 blob_read_from_file(&delta, zOPath);
299 }
300 diff_print_index(zNew);
301 diff_file_mem(&delta, &empty, zOrig, zDiffCmd, 0);
302 }else{
303 Blob a, b, disk;
304 int isOrigLink = file_islink(zOPath);
305 db_ephemeral_blob(&q, 6, &delta);
306 if( isOrigLink ){
307 blob_read_link(&disk, zOPath);
308 }else{
309 blob_read_from_file(&disk, zOPath);
310 }
311 fossil_print("CHANGED %s\n", zNew);
312 if( !isOrigLink != !isLink ){
313 diff_print_index(zNew);
314 printf("--- %s\n+++ %s\n", zOrig, zNew);
315 printf("cannot compute difference between symlink and regular file\n");
316 }else{
317 content_get(rid, &a);
318 blob_delta_apply(&a, &delta, &b);
319 diff_file_mem(&disk, &b, zNew, zDiffCmd, 0);
320 blob_reset(&a);
321 blob_reset(&b);
322 }
323 blob_reset(&disk);
324 }
325 blob_reset(&delta);
326 }
327 db_finalize(&q);
@@ -352,11 +403,11 @@
403 ** Forget everything about STASHID. Forget the whole stash if the
404 ** --all flag is used. Individual drops are undoable but --all is not.
405 **
406 ** fossil stash snapshot ?-m COMMENT? ?FILES...?
407 **
408 ** Save the current changes in the working tree as a new stash
409 ** but, unlike "save", do not revert those changes.
410 **
411 ** fossil stash diff ?STASHID?
412 ** fossil stash gdiff ?STASHID?
413 **
414
+21 -4
--- src/tar.c
+++ src/tar.c
@@ -280,11 +280,12 @@
280280
const char *zName, /* Name of the object */
281281
int nName, /* Number of characters in zName */
282282
int iMode, /* Mode. 0644 or 0755 */
283283
unsigned int mTime, /* File modification time */
284284
int iSize, /* Size of the object in bytes */
285
- char cType /* Type of object. '0'==file. '5'==directory */
285
+ char cType /* Type of object:
286
+ '0'==file. '2'==symlink. '5'==directory */
286287
){
287288
/* set mode and modification time */
288289
sqlite3_snprintf(8, (char*)&tball.aHdr[100], "%07o", iMode);
289290
sqlite3_snprintf(12, (char*)&tball.aHdr[136], "%011o", mTime);
290291
@@ -359,20 +360,36 @@
359360
** Add a single file to the growing tarball.
360361
*/
361362
static void tar_add_file(
362363
const char *zName, /* Name of the file. nul-terminated */
363364
Blob *pContent, /* Content of the file */
364
- int isExe, /* True for executable files */
365
+ int mPerm, /* 1: executable file, 2: symlink */
365366
unsigned int mTime /* Last modification time of the file */
366367
){
367368
int nName = strlen(zName);
368369
int n = blob_size(pContent);
369370
int lastPage;
371
+ char cType = '0';
370372
371373
/* length check moved to tar_split_path */
372374
tar_add_directory_of(zName, nName, mTime);
373
- tar_add_header(zName, nName, isExe ? 0755 : 0644, mTime, n, '0');
375
+
376
+ /*
377
+ * If we have a symlink, write its destination path (which is stored in
378
+ * pContent) into header, and set content length to 0 to avoid storing path
379
+ * as file content in the next step. Since 'linkname' header is limited to
380
+ * 100 bytes (-1 byte for terminating zero), if path is greater than that,
381
+ * store symlink as a plain-text file. (Not sure how TAR handles long links.)
382
+ */
383
+ if( mPerm == PERM_LNK && n <= 100 ){
384
+ sqlite3_snprintf(100, (char*)&tball.aHdr[157], "%s", blob_str(pContent));
385
+ cType = '2';
386
+ n = 0;
387
+ }
388
+
389
+ tar_add_header(zName, nName, ( mPerm==PERM_EXE ) ? 0755 : 0644,
390
+ mTime, n, cType);
374391
if( n ){
375392
gzip_step(blob_buffer(pContent), n);
376393
lastPage = n % 512;
377394
if( lastPage!=0 ){
378395
gzip_step(tball.zSpaces, 512 - lastPage);
@@ -415,11 +432,11 @@
415432
tar_begin();
416433
for(i=3; i<g.argc; i++){
417434
blob_zero(&file);
418435
blob_read_from_file(&file, g.argv[i]);
419436
tar_add_file(g.argv[i], &file,
420
- file_isexe(g.argv[i]), file_mtime(g.argv[i]));
437
+ file_perm(g.argv[i]), file_mtime(g.argv[i]));
421438
blob_reset(&file);
422439
}
423440
tar_finish(&zip);
424441
blob_write_to_file(&zip, g.argv[2]);
425442
}
426443
--- src/tar.c
+++ src/tar.c
@@ -280,11 +280,12 @@
280 const char *zName, /* Name of the object */
281 int nName, /* Number of characters in zName */
282 int iMode, /* Mode. 0644 or 0755 */
283 unsigned int mTime, /* File modification time */
284 int iSize, /* Size of the object in bytes */
285 char cType /* Type of object. '0'==file. '5'==directory */
 
286 ){
287 /* set mode and modification time */
288 sqlite3_snprintf(8, (char*)&tball.aHdr[100], "%07o", iMode);
289 sqlite3_snprintf(12, (char*)&tball.aHdr[136], "%011o", mTime);
290
@@ -359,20 +360,36 @@
359 ** Add a single file to the growing tarball.
360 */
361 static void tar_add_file(
362 const char *zName, /* Name of the file. nul-terminated */
363 Blob *pContent, /* Content of the file */
364 int isExe, /* True for executable files */
365 unsigned int mTime /* Last modification time of the file */
366 ){
367 int nName = strlen(zName);
368 int n = blob_size(pContent);
369 int lastPage;
 
370
371 /* length check moved to tar_split_path */
372 tar_add_directory_of(zName, nName, mTime);
373 tar_add_header(zName, nName, isExe ? 0755 : 0644, mTime, n, '0');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374 if( n ){
375 gzip_step(blob_buffer(pContent), n);
376 lastPage = n % 512;
377 if( lastPage!=0 ){
378 gzip_step(tball.zSpaces, 512 - lastPage);
@@ -415,11 +432,11 @@
415 tar_begin();
416 for(i=3; i<g.argc; i++){
417 blob_zero(&file);
418 blob_read_from_file(&file, g.argv[i]);
419 tar_add_file(g.argv[i], &file,
420 file_isexe(g.argv[i]), file_mtime(g.argv[i]));
421 blob_reset(&file);
422 }
423 tar_finish(&zip);
424 blob_write_to_file(&zip, g.argv[2]);
425 }
426
--- src/tar.c
+++ src/tar.c
@@ -280,11 +280,12 @@
280 const char *zName, /* Name of the object */
281 int nName, /* Number of characters in zName */
282 int iMode, /* Mode. 0644 or 0755 */
283 unsigned int mTime, /* File modification time */
284 int iSize, /* Size of the object in bytes */
285 char cType /* Type of object:
286 '0'==file. '2'==symlink. '5'==directory */
287 ){
288 /* set mode and modification time */
289 sqlite3_snprintf(8, (char*)&tball.aHdr[100], "%07o", iMode);
290 sqlite3_snprintf(12, (char*)&tball.aHdr[136], "%011o", mTime);
291
@@ -359,20 +360,36 @@
360 ** Add a single file to the growing tarball.
361 */
362 static void tar_add_file(
363 const char *zName, /* Name of the file. nul-terminated */
364 Blob *pContent, /* Content of the file */
365 int mPerm, /* 1: executable file, 2: symlink */
366 unsigned int mTime /* Last modification time of the file */
367 ){
368 int nName = strlen(zName);
369 int n = blob_size(pContent);
370 int lastPage;
371 char cType = '0';
372
373 /* length check moved to tar_split_path */
374 tar_add_directory_of(zName, nName, mTime);
375
376 /*
377 * If we have a symlink, write its destination path (which is stored in
378 * pContent) into header, and set content length to 0 to avoid storing path
379 * as file content in the next step. Since 'linkname' header is limited to
380 * 100 bytes (-1 byte for terminating zero), if path is greater than that,
381 * store symlink as a plain-text file. (Not sure how TAR handles long links.)
382 */
383 if( mPerm == PERM_LNK && n <= 100 ){
384 sqlite3_snprintf(100, (char*)&tball.aHdr[157], "%s", blob_str(pContent));
385 cType = '2';
386 n = 0;
387 }
388
389 tar_add_header(zName, nName, ( mPerm==PERM_EXE ) ? 0755 : 0644,
390 mTime, n, cType);
391 if( n ){
392 gzip_step(blob_buffer(pContent), n);
393 lastPage = n % 512;
394 if( lastPage!=0 ){
395 gzip_step(tball.zSpaces, 512 - lastPage);
@@ -415,11 +432,11 @@
432 tar_begin();
433 for(i=3; i<g.argc; i++){
434 blob_zero(&file);
435 blob_read_from_file(&file, g.argv[i]);
436 tar_add_file(g.argv[i], &file,
437 file_perm(g.argv[i]), file_mtime(g.argv[i]));
438 blob_reset(&file);
439 }
440 tar_finish(&zip);
441 blob_write_to_file(&zip, g.argv[2]);
442 }
443
+30 -9
--- src/undo.c
+++ src/undo.c
@@ -30,25 +30,33 @@
3030
*/
3131
static void undo_one(const char *zPathname, int redoFlag){
3232
Stmt q;
3333
char *zFullname;
3434
db_prepare(&q,
35
- "SELECT content, existsflag, isExe FROM undo"
35
+ "SELECT content, existsflag, isExe, isLink FROM undo"
3636
" WHERE pathname=%Q AND redoflag=%d",
3737
zPathname, redoFlag
3838
);
3939
if( db_step(&q)==SQLITE_ROW ){
4040
int old_exists;
4141
int new_exists;
4242
int old_exe;
4343
int new_exe;
44
+ int new_link;
45
+ int old_link;
4446
Blob current;
4547
Blob new;
4648
zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
49
+ old_link = db_column_int(&q, 3);
50
+ new_link = file_islink(zFullname);
4751
new_exists = file_size(zFullname)>=0;
4852
if( new_exists ){
49
- blob_read_from_file(&current, zFullname);
53
+ if( new_link ){
54
+ blob_read_link(&current, zFullname);
55
+ }else{
56
+ blob_read_from_file(&current, zFullname);
57
+ }
5058
new_exe = file_isexe(zFullname);
5159
}else{
5260
blob_zero(&current);
5361
new_exe = 0;
5462
}
@@ -62,24 +70,31 @@
6270
if( new_exists ){
6371
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
6472
}else{
6573
fossil_print("NEW %s\n", zPathname);
6674
}
67
- blob_write_to_file(&new, zFullname);
75
+ if( new_exists && (new_link || old_link) ){
76
+ unlink(zFullname);
77
+ }
78
+ if( new_link ){
79
+ create_symlink(blob_str(&new), zFullname);
80
+ }else{
81
+ blob_write_to_file(&new, zFullname);
82
+ }
6883
file_setexe(zFullname, old_exe);
6984
}else{
7085
fossil_print("DELETE %s\n", zPathname);
7186
file_delete(zFullname);
7287
}
7388
blob_reset(&new);
7489
free(zFullname);
7590
db_finalize(&q);
7691
db_prepare(&q,
77
- "UPDATE undo SET content=:c, existsflag=%d, isExe=%d,"
92
+ "UPDATE undo SET content=:c, existsflag=%d, isExe=%d, isLink=%d,"
7893
" redoflag=NOT redoflag"
7994
" WHERE pathname=%Q",
80
- new_exists, new_exe, zPathname
95
+ new_exists, new_exe, new_link, zPathname
8196
);
8297
if( new_exists ){
8398
db_bind_blob(&q, ":c", &current);
8499
}
85100
db_step(&q);
@@ -209,10 +224,11 @@
209224
@ CREATE TABLE %s.undo(
210225
@ pathname TEXT UNIQUE, -- Name of the file
211226
@ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable
212227
@ existsflag BOOLEAN, -- True if the file exists
213228
@ isExe BOOLEAN, -- True if the file is executable
229
+ @ isLink BOOLEAN, -- True if the file is symlink
214230
@ content BLOB -- Saved content
215231
@ );
216232
@ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
217233
@ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
218234
;
@@ -249,22 +265,27 @@
249265
*/
250266
void undo_save(const char *zPathname){
251267
char *zFullname;
252268
Blob content;
253269
int existsFlag;
270
+ int isLink;
254271
Stmt q;
255272
256273
if( !undoActive ) return;
257274
zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
258275
existsFlag = file_size(zFullname)>=0;
259276
db_prepare(&q,
260
- "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,isExe,content)"
261
- " VALUES(%Q,0,%d,%d,:c)",
262
- zPathname, existsFlag, file_isexe(zFullname)
277
+ "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,isExe,isLink,content)"
278
+ " VALUES(%Q,0,%d,%d,%d,:c)",
279
+ zPathname, existsFlag, file_isexe(zFullname), file_islink(zFullname)
263280
);
264281
if( existsFlag ){
265
- blob_read_from_file(&content, zFullname);
282
+ if( isLink ){
283
+ blob_read_link(&content, zFullname);
284
+ }else{
285
+ blob_read_from_file(&content, zFullname);
286
+ }
266287
db_bind_blob(&q, ":c", &content);
267288
}
268289
free(zFullname);
269290
db_step(&q);
270291
db_finalize(&q);
271292
--- src/undo.c
+++ src/undo.c
@@ -30,25 +30,33 @@
30 */
31 static void undo_one(const char *zPathname, int redoFlag){
32 Stmt q;
33 char *zFullname;
34 db_prepare(&q,
35 "SELECT content, existsflag, isExe FROM undo"
36 " WHERE pathname=%Q AND redoflag=%d",
37 zPathname, redoFlag
38 );
39 if( db_step(&q)==SQLITE_ROW ){
40 int old_exists;
41 int new_exists;
42 int old_exe;
43 int new_exe;
 
 
44 Blob current;
45 Blob new;
46 zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
 
 
47 new_exists = file_size(zFullname)>=0;
48 if( new_exists ){
49 blob_read_from_file(&current, zFullname);
 
 
 
 
50 new_exe = file_isexe(zFullname);
51 }else{
52 blob_zero(&current);
53 new_exe = 0;
54 }
@@ -62,24 +70,31 @@
62 if( new_exists ){
63 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
64 }else{
65 fossil_print("NEW %s\n", zPathname);
66 }
67 blob_write_to_file(&new, zFullname);
 
 
 
 
 
 
 
68 file_setexe(zFullname, old_exe);
69 }else{
70 fossil_print("DELETE %s\n", zPathname);
71 file_delete(zFullname);
72 }
73 blob_reset(&new);
74 free(zFullname);
75 db_finalize(&q);
76 db_prepare(&q,
77 "UPDATE undo SET content=:c, existsflag=%d, isExe=%d,"
78 " redoflag=NOT redoflag"
79 " WHERE pathname=%Q",
80 new_exists, new_exe, zPathname
81 );
82 if( new_exists ){
83 db_bind_blob(&q, ":c", &current);
84 }
85 db_step(&q);
@@ -209,10 +224,11 @@
209 @ CREATE TABLE %s.undo(
210 @ pathname TEXT UNIQUE, -- Name of the file
211 @ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable
212 @ existsflag BOOLEAN, -- True if the file exists
213 @ isExe BOOLEAN, -- True if the file is executable
 
214 @ content BLOB -- Saved content
215 @ );
216 @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
217 @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
218 ;
@@ -249,22 +265,27 @@
249 */
250 void undo_save(const char *zPathname){
251 char *zFullname;
252 Blob content;
253 int existsFlag;
 
254 Stmt q;
255
256 if( !undoActive ) return;
257 zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
258 existsFlag = file_size(zFullname)>=0;
259 db_prepare(&q,
260 "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,isExe,content)"
261 " VALUES(%Q,0,%d,%d,:c)",
262 zPathname, existsFlag, file_isexe(zFullname)
263 );
264 if( existsFlag ){
265 blob_read_from_file(&content, zFullname);
 
 
 
 
266 db_bind_blob(&q, ":c", &content);
267 }
268 free(zFullname);
269 db_step(&q);
270 db_finalize(&q);
271
--- src/undo.c
+++ src/undo.c
@@ -30,25 +30,33 @@
30 */
31 static void undo_one(const char *zPathname, int redoFlag){
32 Stmt q;
33 char *zFullname;
34 db_prepare(&q,
35 "SELECT content, existsflag, isExe, isLink FROM undo"
36 " WHERE pathname=%Q AND redoflag=%d",
37 zPathname, redoFlag
38 );
39 if( db_step(&q)==SQLITE_ROW ){
40 int old_exists;
41 int new_exists;
42 int old_exe;
43 int new_exe;
44 int new_link;
45 int old_link;
46 Blob current;
47 Blob new;
48 zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
49 old_link = db_column_int(&q, 3);
50 new_link = file_islink(zFullname);
51 new_exists = file_size(zFullname)>=0;
52 if( new_exists ){
53 if( new_link ){
54 blob_read_link(&current, zFullname);
55 }else{
56 blob_read_from_file(&current, zFullname);
57 }
58 new_exe = file_isexe(zFullname);
59 }else{
60 blob_zero(&current);
61 new_exe = 0;
62 }
@@ -62,24 +70,31 @@
70 if( new_exists ){
71 fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
72 }else{
73 fossil_print("NEW %s\n", zPathname);
74 }
75 if( new_exists && (new_link || old_link) ){
76 unlink(zFullname);
77 }
78 if( new_link ){
79 create_symlink(blob_str(&new), zFullname);
80 }else{
81 blob_write_to_file(&new, zFullname);
82 }
83 file_setexe(zFullname, old_exe);
84 }else{
85 fossil_print("DELETE %s\n", zPathname);
86 file_delete(zFullname);
87 }
88 blob_reset(&new);
89 free(zFullname);
90 db_finalize(&q);
91 db_prepare(&q,
92 "UPDATE undo SET content=:c, existsflag=%d, isExe=%d, isLink=%d,"
93 " redoflag=NOT redoflag"
94 " WHERE pathname=%Q",
95 new_exists, new_exe, new_link, zPathname
96 );
97 if( new_exists ){
98 db_bind_blob(&q, ":c", &current);
99 }
100 db_step(&q);
@@ -209,10 +224,11 @@
224 @ CREATE TABLE %s.undo(
225 @ pathname TEXT UNIQUE, -- Name of the file
226 @ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable
227 @ existsflag BOOLEAN, -- True if the file exists
228 @ isExe BOOLEAN, -- True if the file is executable
229 @ isLink BOOLEAN, -- True if the file is symlink
230 @ content BLOB -- Saved content
231 @ );
232 @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile;
233 @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge;
234 ;
@@ -249,22 +265,27 @@
265 */
266 void undo_save(const char *zPathname){
267 char *zFullname;
268 Blob content;
269 int existsFlag;
270 int isLink;
271 Stmt q;
272
273 if( !undoActive ) return;
274 zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
275 existsFlag = file_size(zFullname)>=0;
276 db_prepare(&q,
277 "INSERT OR IGNORE INTO undo(pathname,redoflag,existsflag,isExe,isLink,content)"
278 " VALUES(%Q,0,%d,%d,%d,:c)",
279 zPathname, existsFlag, file_isexe(zFullname), file_islink(zFullname)
280 );
281 if( existsFlag ){
282 if( isLink ){
283 blob_read_link(&content, zFullname);
284 }else{
285 blob_read_from_file(&content, zFullname);
286 }
287 db_bind_blob(&q, ":c", &content);
288 }
289 free(zFullname);
290 db_step(&q);
291 db_finalize(&q);
292
+61 -28
--- src/update.c
+++ src/update.c
@@ -202,10 +202,12 @@
202202
"CREATE TEMP TABLE fv("
203203
" fn TEXT PRIMARY KEY," /* The filename relative to root */
204204
" idv INTEGER," /* VFILE entry for current version */
205205
" idt INTEGER," /* VFILE entry for target version */
206206
" chnged BOOLEAN," /* True if current version has been edited */
207
+ " islinkv BOOLEAN," /* True if current file is a link */
208
+ " islinkt BOOLEAN," /* True if target file is a link */
207209
" ridv INTEGER," /* Record ID for current version */
208210
" ridt INTEGER," /* Record ID for target */
209211
" isexe BOOLEAN," /* Does target have execute permission? */
210212
" fnt TEXT" /* Filename of same file on target version */
211213
");"
@@ -254,22 +256,35 @@
254256
"UPDATE fv SET"
255257
" idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnt),0),"
256258
" ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnt),0)",
257259
tid, tid
258260
);
261
+
262
+ /*
263
+ ** Add islink information
264
+ */
265
+ db_multi_exec(
266
+ "UPDATE fv SET"
267
+ " islinkv=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnt),0),"
268
+ " islinkt=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnt),0)",
269
+ vid, tid
270
+ );
271
+
259272
260273
if( debugFlag ){
261274
db_prepare(&q,
262
- "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe FROM fv"
275
+ "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe, islinkv, islinkt FROM fv"
263276
);
264277
while( db_step(&q)==SQLITE_ROW ){
265
- fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d\n",
278
+ fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d islinkv=%d islinkt=%d\n",
266279
db_column_int(&q, 0),
267280
db_column_int(&q, 4),
268281
db_column_int(&q, 5),
269282
db_column_int(&q, 3),
270
- db_column_int(&q, 6));
283
+ db_column_int(&q, 6),
284
+ db_column_int(&q, 7),
285
+ db_column_int(&q, 8));
271286
fossil_print(" fnv = [%s]\n", db_column_text(&q, 1));
272287
fossil_print(" fnt = [%s]\n", db_column_text(&q, 2));
273288
}
274289
db_finalize(&q);
275290
}
@@ -309,11 +324,11 @@
309324
/*
310325
** Alter the content of the checkout so that it conforms with the
311326
** target
312327
*/
313328
db_prepare(&q,
314
- "SELECT fn, idv, ridv, idt, ridt, chnged, fnt, isexe FROM fv ORDER BY 1"
329
+ "SELECT fn, idv, ridv, idt, ridt, chnged, fnt, isexe, islinkv, islinkt FROM fv ORDER BY 1"
315330
);
316331
db_prepare(&mtimeXfer,
317332
"UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
318333
" WHERE id=:idt"
319334
);
@@ -327,10 +342,12 @@
327342
int idt = db_column_int(&q, 3); /* VFILE entry for target */
328343
int ridt = db_column_int(&q, 4); /* RecordID for target */
329344
int chnged = db_column_int(&q, 5); /* Current is edited */
330345
const char *zNewName = db_column_text(&q,6);/* New filename */
331346
int isexe = db_column_int(&q, 7); /* EXE perm for new file */
347
+ int islinkv = db_column_int(&q, 8); /* Is current file is a link */
348
+ int islinkt = db_column_int(&q, 9); /* Is target file is a link */
332349
char *zFullPath; /* Full pathname of the file */
333350
char *zFullNewPath; /* Full pathname of dest */
334351
char nameChng; /* True if the name changed */
335352
336353
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
@@ -381,30 +398,36 @@
381398
if( nameChng ){
382399
fossil_print("MERGE %s -> %s\n", zName, zNewName);
383400
}else{
384401
fossil_print("MERGE %s\n", zName);
385402
}
386
- undo_save(zName);
387
- content_get(ridt, &t);
388
- content_get(ridv, &v);
389
- rc = merge_3way(&v, zFullPath, &t, &r);
390
- if( rc>=0 ){
391
- if( !nochangeFlag ){
392
- blob_write_to_file(&r, zFullNewPath);
393
- file_setexe(zFullNewPath, isexe);
394
- }
395
- if( rc>0 ){
396
- fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
397
- nConflict++;
398
- }
403
+ if( islinkv || islinkt /* || file_islink(zFullPath) */ ){
404
+ //if( !nochangeFlag ) blob_write_to_file(&t, zFullNewPath);
405
+ fossil_print("***** Cannot merge symlink %s\n", zNewName);
406
+ nConflict++;
399407
}else{
400
- if( !nochangeFlag ){
401
- blob_write_to_file(&t, zFullNewPath);
402
- file_setexe(zFullNewPath, isexe);
403
- }
404
- fossil_print("***** Cannot merge binary file %s\n", zNewName);
405
- nConflict++;
408
+ undo_save(zName);
409
+ content_get(ridt, &t);
410
+ content_get(ridv, &v);
411
+ rc = merge_3way(&v, zFullPath, &t, &r);
412
+ if( rc>=0 ){
413
+ if( !nochangeFlag ){
414
+ blob_write_to_file(&r, zFullNewPath);
415
+ file_setexe(zFullNewPath, isexe);
416
+ }
417
+ if( rc>0 ){
418
+ fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
419
+ nConflict++;
420
+ }
421
+ }else{
422
+ if( !nochangeFlag ){
423
+ blob_write_to_file(&t, zFullNewPath);
424
+ file_setexe(zFullNewPath, isexe);
425
+ }
426
+ fossil_print("***** Cannot merge binary file %s\n", zNewName);
427
+ nConflict++;
428
+ }
406429
}
407430
if( nameChng && !nochangeFlag ) file_delete(zFullPath);
408431
blob_reset(&v);
409432
blob_reset(&t);
410433
blob_reset(&r);
@@ -522,10 +545,11 @@
522545
*/
523546
int historical_version_of_file(
524547
const char *revision, /* The checkin containing the file */
525548
const char *file, /* Full treename of the file */
526549
Blob *content, /* Put the content here */
550
+ int *pIsLink, /* Set to true if file is link. */
527551
int *pIsExe, /* Set to true if file is executable */
528552
int errCode /* Error code if file not found. Panic if 0. */
529553
){
530554
Manifest *pManifest;
531555
ManifestFile *pFile;
@@ -544,11 +568,12 @@
544568
545569
if( pManifest ){
546570
pFile = manifest_file_seek(pManifest, file);
547571
if( pFile ){
548572
rid = uuid_to_rid(pFile->zUuid, 0);
549
- if( pIsExe ) *pIsExe = manifest_file_mperm(pFile);
573
+ if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
574
+ if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
550575
manifest_destroy(pManifest);
551576
return content_get(rid, content);
552577
}
553578
manifest_destroy(pManifest);
554579
if( errCode<=0 ){
@@ -627,14 +652,15 @@
627652
int vid = db_lget_int("checkout", 0);
628653
zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
629654
}
630655
while( db_step(&q)==SQLITE_ROW ){
631656
int isExe = 0;
657
+ int isLink = 0;
632658
char *zFull;
633659
zFile = db_column_text(&q, 0);
634660
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
635
- errCode = historical_version_of_file(zRevision, zFile, &record, &isExe,2);
661
+ errCode = historical_version_of_file(zRevision, zFile, &record, &isLink, &isExe,2);
636662
if( errCode==2 ){
637663
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
638664
fossil_print("UNMANAGE: %s\n", zFile);
639665
}else{
640666
undo_save(zFile);
@@ -643,24 +669,31 @@
643669
}
644670
db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile);
645671
}else{
646672
sqlite3_int64 mtime;
647673
undo_save(zFile);
648
- blob_write_to_file(&record, zFull);
674
+ if( file_size(zFull)>=0 && (isLink || file_islink(zFull)) ){
675
+ unlink(zFull);
676
+ }
677
+ if( isLink ){
678
+ create_symlink(blob_str(&record), zFull);
679
+ }else{
680
+ blob_write_to_file(&record, zFull);
681
+ }
649682
file_setexe(zFull, isExe);
650683
fossil_print("REVERTED: %s\n", zFile);
651684
mtime = file_mtime(zFull);
652685
db_multi_exec(
653686
"UPDATE vfile"
654
- " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, mrid=rid,"
687
+ " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d, mrid=rid,"
655688
" pathname=coalesce(origname,pathname), origname=NULL"
656689
" WHERE pathname=%Q",
657
- mtime, isExe, zFile
690
+ mtime, isExe, isLink, zFile
658691
);
659692
}
660693
blob_reset(&record);
661694
free(zFull);
662695
}
663696
db_finalize(&q);
664697
undo_finish();
665698
db_end_transaction(0);
666699
}
667700
--- src/update.c
+++ src/update.c
@@ -202,10 +202,12 @@
202 "CREATE TEMP TABLE fv("
203 " fn TEXT PRIMARY KEY," /* The filename relative to root */
204 " idv INTEGER," /* VFILE entry for current version */
205 " idt INTEGER," /* VFILE entry for target version */
206 " chnged BOOLEAN," /* True if current version has been edited */
 
 
207 " ridv INTEGER," /* Record ID for current version */
208 " ridt INTEGER," /* Record ID for target */
209 " isexe BOOLEAN," /* Does target have execute permission? */
210 " fnt TEXT" /* Filename of same file on target version */
211 ");"
@@ -254,22 +256,35 @@
254 "UPDATE fv SET"
255 " idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnt),0),"
256 " ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnt),0)",
257 tid, tid
258 );
 
 
 
 
 
 
 
 
 
 
 
259
260 if( debugFlag ){
261 db_prepare(&q,
262 "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe FROM fv"
263 );
264 while( db_step(&q)==SQLITE_ROW ){
265 fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d\n",
266 db_column_int(&q, 0),
267 db_column_int(&q, 4),
268 db_column_int(&q, 5),
269 db_column_int(&q, 3),
270 db_column_int(&q, 6));
 
 
271 fossil_print(" fnv = [%s]\n", db_column_text(&q, 1));
272 fossil_print(" fnt = [%s]\n", db_column_text(&q, 2));
273 }
274 db_finalize(&q);
275 }
@@ -309,11 +324,11 @@
309 /*
310 ** Alter the content of the checkout so that it conforms with the
311 ** target
312 */
313 db_prepare(&q,
314 "SELECT fn, idv, ridv, idt, ridt, chnged, fnt, isexe FROM fv ORDER BY 1"
315 );
316 db_prepare(&mtimeXfer,
317 "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
318 " WHERE id=:idt"
319 );
@@ -327,10 +342,12 @@
327 int idt = db_column_int(&q, 3); /* VFILE entry for target */
328 int ridt = db_column_int(&q, 4); /* RecordID for target */
329 int chnged = db_column_int(&q, 5); /* Current is edited */
330 const char *zNewName = db_column_text(&q,6);/* New filename */
331 int isexe = db_column_int(&q, 7); /* EXE perm for new file */
 
 
332 char *zFullPath; /* Full pathname of the file */
333 char *zFullNewPath; /* Full pathname of dest */
334 char nameChng; /* True if the name changed */
335
336 zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
@@ -381,30 +398,36 @@
381 if( nameChng ){
382 fossil_print("MERGE %s -> %s\n", zName, zNewName);
383 }else{
384 fossil_print("MERGE %s\n", zName);
385 }
386 undo_save(zName);
387 content_get(ridt, &t);
388 content_get(ridv, &v);
389 rc = merge_3way(&v, zFullPath, &t, &r);
390 if( rc>=0 ){
391 if( !nochangeFlag ){
392 blob_write_to_file(&r, zFullNewPath);
393 file_setexe(zFullNewPath, isexe);
394 }
395 if( rc>0 ){
396 fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
397 nConflict++;
398 }
399 }else{
400 if( !nochangeFlag ){
401 blob_write_to_file(&t, zFullNewPath);
402 file_setexe(zFullNewPath, isexe);
403 }
404 fossil_print("***** Cannot merge binary file %s\n", zNewName);
405 nConflict++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406 }
407 if( nameChng && !nochangeFlag ) file_delete(zFullPath);
408 blob_reset(&v);
409 blob_reset(&t);
410 blob_reset(&r);
@@ -522,10 +545,11 @@
522 */
523 int historical_version_of_file(
524 const char *revision, /* The checkin containing the file */
525 const char *file, /* Full treename of the file */
526 Blob *content, /* Put the content here */
 
527 int *pIsExe, /* Set to true if file is executable */
528 int errCode /* Error code if file not found. Panic if 0. */
529 ){
530 Manifest *pManifest;
531 ManifestFile *pFile;
@@ -544,11 +568,12 @@
544
545 if( pManifest ){
546 pFile = manifest_file_seek(pManifest, file);
547 if( pFile ){
548 rid = uuid_to_rid(pFile->zUuid, 0);
549 if( pIsExe ) *pIsExe = manifest_file_mperm(pFile);
 
550 manifest_destroy(pManifest);
551 return content_get(rid, content);
552 }
553 manifest_destroy(pManifest);
554 if( errCode<=0 ){
@@ -627,14 +652,15 @@
627 int vid = db_lget_int("checkout", 0);
628 zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
629 }
630 while( db_step(&q)==SQLITE_ROW ){
631 int isExe = 0;
 
632 char *zFull;
633 zFile = db_column_text(&q, 0);
634 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
635 errCode = historical_version_of_file(zRevision, zFile, &record, &isExe,2);
636 if( errCode==2 ){
637 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
638 fossil_print("UNMANAGE: %s\n", zFile);
639 }else{
640 undo_save(zFile);
@@ -643,24 +669,31 @@
643 }
644 db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile);
645 }else{
646 sqlite3_int64 mtime;
647 undo_save(zFile);
648 blob_write_to_file(&record, zFull);
 
 
 
 
 
 
 
649 file_setexe(zFull, isExe);
650 fossil_print("REVERTED: %s\n", zFile);
651 mtime = file_mtime(zFull);
652 db_multi_exec(
653 "UPDATE vfile"
654 " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, mrid=rid,"
655 " pathname=coalesce(origname,pathname), origname=NULL"
656 " WHERE pathname=%Q",
657 mtime, isExe, zFile
658 );
659 }
660 blob_reset(&record);
661 free(zFull);
662 }
663 db_finalize(&q);
664 undo_finish();
665 db_end_transaction(0);
666 }
667
--- src/update.c
+++ src/update.c
@@ -202,10 +202,12 @@
202 "CREATE TEMP TABLE fv("
203 " fn TEXT PRIMARY KEY," /* The filename relative to root */
204 " idv INTEGER," /* VFILE entry for current version */
205 " idt INTEGER," /* VFILE entry for target version */
206 " chnged BOOLEAN," /* True if current version has been edited */
207 " islinkv BOOLEAN," /* True if current file is a link */
208 " islinkt BOOLEAN," /* True if target file is a link */
209 " ridv INTEGER," /* Record ID for current version */
210 " ridt INTEGER," /* Record ID for target */
211 " isexe BOOLEAN," /* Does target have execute permission? */
212 " fnt TEXT" /* Filename of same file on target version */
213 ");"
@@ -254,22 +256,35 @@
256 "UPDATE fv SET"
257 " idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnt),0),"
258 " ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnt),0)",
259 tid, tid
260 );
261
262 /*
263 ** Add islink information
264 */
265 db_multi_exec(
266 "UPDATE fv SET"
267 " islinkv=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnt),0),"
268 " islinkt=coalesce((SELECT islink FROM vfile WHERE vid=%d AND pathname=fnt),0)",
269 vid, tid
270 );
271
272
273 if( debugFlag ){
274 db_prepare(&q,
275 "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe, islinkv, islinkt FROM fv"
276 );
277 while( db_step(&q)==SQLITE_ROW ){
278 fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d islinkv=%d islinkt=%d\n",
279 db_column_int(&q, 0),
280 db_column_int(&q, 4),
281 db_column_int(&q, 5),
282 db_column_int(&q, 3),
283 db_column_int(&q, 6),
284 db_column_int(&q, 7),
285 db_column_int(&q, 8));
286 fossil_print(" fnv = [%s]\n", db_column_text(&q, 1));
287 fossil_print(" fnt = [%s]\n", db_column_text(&q, 2));
288 }
289 db_finalize(&q);
290 }
@@ -309,11 +324,11 @@
324 /*
325 ** Alter the content of the checkout so that it conforms with the
326 ** target
327 */
328 db_prepare(&q,
329 "SELECT fn, idv, ridv, idt, ridt, chnged, fnt, isexe, islinkv, islinkt FROM fv ORDER BY 1"
330 );
331 db_prepare(&mtimeXfer,
332 "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
333 " WHERE id=:idt"
334 );
@@ -327,10 +342,12 @@
342 int idt = db_column_int(&q, 3); /* VFILE entry for target */
343 int ridt = db_column_int(&q, 4); /* RecordID for target */
344 int chnged = db_column_int(&q, 5); /* Current is edited */
345 const char *zNewName = db_column_text(&q,6);/* New filename */
346 int isexe = db_column_int(&q, 7); /* EXE perm for new file */
347 int islinkv = db_column_int(&q, 8); /* Is current file is a link */
348 int islinkt = db_column_int(&q, 9); /* Is target file is a link */
349 char *zFullPath; /* Full pathname of the file */
350 char *zFullNewPath; /* Full pathname of dest */
351 char nameChng; /* True if the name changed */
352
353 zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
@@ -381,30 +398,36 @@
398 if( nameChng ){
399 fossil_print("MERGE %s -> %s\n", zName, zNewName);
400 }else{
401 fossil_print("MERGE %s\n", zName);
402 }
403 if( islinkv || islinkt /* || file_islink(zFullPath) */ ){
404 //if( !nochangeFlag ) blob_write_to_file(&t, zFullNewPath);
405 fossil_print("***** Cannot merge symlink %s\n", zNewName);
406 nConflict++;
 
 
 
 
 
 
 
 
 
407 }else{
408 undo_save(zName);
409 content_get(ridt, &t);
410 content_get(ridv, &v);
411 rc = merge_3way(&v, zFullPath, &t, &r);
412 if( rc>=0 ){
413 if( !nochangeFlag ){
414 blob_write_to_file(&r, zFullNewPath);
415 file_setexe(zFullNewPath, isexe);
416 }
417 if( rc>0 ){
418 fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
419 nConflict++;
420 }
421 }else{
422 if( !nochangeFlag ){
423 blob_write_to_file(&t, zFullNewPath);
424 file_setexe(zFullNewPath, isexe);
425 }
426 fossil_print("***** Cannot merge binary file %s\n", zNewName);
427 nConflict++;
428 }
429 }
430 if( nameChng && !nochangeFlag ) file_delete(zFullPath);
431 blob_reset(&v);
432 blob_reset(&t);
433 blob_reset(&r);
@@ -522,10 +545,11 @@
545 */
546 int historical_version_of_file(
547 const char *revision, /* The checkin containing the file */
548 const char *file, /* Full treename of the file */
549 Blob *content, /* Put the content here */
550 int *pIsLink, /* Set to true if file is link. */
551 int *pIsExe, /* Set to true if file is executable */
552 int errCode /* Error code if file not found. Panic if 0. */
553 ){
554 Manifest *pManifest;
555 ManifestFile *pFile;
@@ -544,11 +568,12 @@
568
569 if( pManifest ){
570 pFile = manifest_file_seek(pManifest, file);
571 if( pFile ){
572 rid = uuid_to_rid(pFile->zUuid, 0);
573 if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
574 if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
575 manifest_destroy(pManifest);
576 return content_get(rid, content);
577 }
578 manifest_destroy(pManifest);
579 if( errCode<=0 ){
@@ -627,14 +652,15 @@
652 int vid = db_lget_int("checkout", 0);
653 zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
654 }
655 while( db_step(&q)==SQLITE_ROW ){
656 int isExe = 0;
657 int isLink = 0;
658 char *zFull;
659 zFile = db_column_text(&q, 0);
660 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
661 errCode = historical_version_of_file(zRevision, zFile, &record, &isLink, &isExe,2);
662 if( errCode==2 ){
663 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
664 fossil_print("UNMANAGE: %s\n", zFile);
665 }else{
666 undo_save(zFile);
@@ -643,24 +669,31 @@
669 }
670 db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile);
671 }else{
672 sqlite3_int64 mtime;
673 undo_save(zFile);
674 if( file_size(zFull)>=0 && (isLink || file_islink(zFull)) ){
675 unlink(zFull);
676 }
677 if( isLink ){
678 create_symlink(blob_str(&record), zFull);
679 }else{
680 blob_write_to_file(&record, zFull);
681 }
682 file_setexe(zFull, isExe);
683 fossil_print("REVERTED: %s\n", zFile);
684 mtime = file_mtime(zFull);
685 db_multi_exec(
686 "UPDATE vfile"
687 " SET mtime=%lld, chnged=0, deleted=0, isexe=%d, islink=%d, mrid=rid,"
688 " pathname=coalesce(origname,pathname), origname=NULL"
689 " WHERE pathname=%Q",
690 mtime, isExe, isLink, zFile
691 );
692 }
693 blob_reset(&record);
694 free(zFull);
695 }
696 db_finalize(&q);
697 undo_finish();
698 db_end_transaction(0);
699 }
700
+55 -27
--- src/vfile.c
+++ src/vfile.c
@@ -90,12 +90,12 @@
9090
db_begin_transaction();
9191
p = manifest_get(vid, CFTYPE_MANIFEST);
9292
if( p==0 ) return;
9393
db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
9494
db_prepare(&ins,
95
- "INSERT INTO vfile(vid,isexe,rid,mrid,pathname) "
96
- " VALUES(:vid,:isexe,:id,:id,:name)");
95
+ "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname) "
96
+ " VALUES(:vid,:isexe,:islink,:id,:id,:name)");
9797
db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid");
9898
db_bind_int(&ins, ":vid", vid);
9999
manifest_file_rewind(p);
100100
while( (pFile = manifest_file_next(p,0))!=0 ){
101101
if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
@@ -110,13 +110,14 @@
110110
db_reset(&ridq);
111111
if( rid==0 || size<0 ){
112112
fossil_warning("content missing for %s", pFile->zName);
113113
continue;
114114
}
115
- db_bind_int(&ins, ":isexe", manifest_file_mperm(pFile));
115
+ db_bind_int(&ins, ":isexe", ( manifest_file_mperm(pFile)==PERM_EXE ));
116116
db_bind_int(&ins, ":id", rid);
117117
db_bind_text(&ins, ":name", pFile->zName);
118
+ db_bind_int(&ins, ":islink", ( manifest_file_mperm(pFile)==PERM_LNK ));
118119
db_step(&ins);
119120
db_reset(&ins);
120121
}
121122
db_finalize(&ridq);
122123
db_finalize(&ins);
@@ -165,11 +166,11 @@
165166
isDeleted = db_column_int(&q, 3);
166167
oldChnged = db_column_int(&q, 4);
167168
oldMtime = db_column_int64(&q, 7);
168169
if( isDeleted ){
169170
chnged = 1;
170
- }else if( !file_isfile(zName) && file_size(0)>=0 ){
171
+ }else if( !file_isfile_or_link(zName) && file_size(0)>=0 ){
171172
if( notFileIsFatal ){
172173
fossil_warning("not an ordinary file: %s", zName);
173174
nErr++;
174175
}
175176
chnged = 1;
@@ -223,29 +224,30 @@
223224
Stmt q;
224225
Blob content;
225226
int nRepos = strlen(g.zLocalRoot);
226227
227228
if( vid>0 && id==0 ){
228
- db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
229
+ db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
229230
" FROM vfile"
230231
" WHERE vid=%d AND mrid>0",
231232
g.zLocalRoot, vid);
232233
}else{
233234
assert( vid==0 && id>0 );
234
- db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
235
+ db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
235236
" FROM vfile"
236237
" WHERE id=%d AND mrid>0",
237238
g.zLocalRoot, id);
238239
}
239240
while( db_step(&q)==SQLITE_ROW ){
240
- int id, rid, isExe;
241
+ int id, rid, isExe, isLink;
241242
const char *zName;
242243
243244
id = db_column_int(&q, 0);
244245
zName = db_column_text(&q, 1);
245246
rid = db_column_int(&q, 2);
246247
isExe = db_column_int(&q, 3);
248
+ isLink = db_column_int(&q, 4);
247249
content_get(rid, &content);
248250
if( file_is_the_same(&content, zName) ){
249251
blob_reset(&content);
250252
if( file_setexe(zName, isExe) ){
251253
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
@@ -270,11 +272,22 @@
270272
blob_reset(&content);
271273
continue;
272274
}
273275
}
274276
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
275
- blob_write_to_file(&content, zName);
277
+ if( file_isdir(zName) == 1 ){
278
+ /*TODO(dchest): remove directories? */
279
+ fossil_fatal("%s is directory, cannot overwrite\n", zName);
280
+ }
281
+ if( file_size(zName)>=0 && (isLink || file_islink(zName)) ){
282
+ file_delete(zName);
283
+ }
284
+ if( isLink ){
285
+ create_symlink(blob_str(&content), zName);
286
+ }else{
287
+ blob_write_to_file(&content, zName);
288
+ }
276289
file_setexe(zName, isExe);
277290
blob_reset(&content);
278291
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
279292
file_mtime(zName), id);
280293
}
@@ -380,11 +393,11 @@
380393
/* do nothing */
381394
}else if( file_isdir(zPath)==1 ){
382395
if( !vfile_top_of_checkout(zPath) ){
383396
vfile_scan(pPath, nPrefix, allFlag, pIgnore);
384397
}
385
- }else if( file_isfile(zPath) ){
398
+ }else if( file_isfile_or_link(zPath) ){
386399
db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
387400
db_step(&ins);
388401
db_reset(&ins);
389402
}
390403
blob_resize(pPath, origSize);
@@ -439,27 +452,38 @@
439452
const char *zName = db_column_text(&q, 1);
440453
int isSelected = db_column_int(&q, 3);
441454
442455
if( isSelected ){
443456
md5sum_step_text(zName, -1);
444
- in = fossil_fopen(zFullpath,"rb");
445
- if( in==0 ){
446
- md5sum_step_text(" 0\n", -1);
447
- continue;
448
- }
449
- fseek(in, 0L, SEEK_END);
450
- sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
451
- fseek(in, 0L, SEEK_SET);
452
- md5sum_step_text(zBuf, -1);
453
- /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
454
- for(;;){
455
- int n;
456
- n = fread(zBuf, 1, sizeof(zBuf), in);
457
- if( n<=0 ) break;
458
- md5sum_step_text(zBuf, n);
459
- }
460
- fclose(in);
457
+ if( file_islink(zFullpath) ){
458
+ /* Instead of file content, use link destination path */
459
+ Blob pathBuf;
460
+
461
+ sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n",
462
+ blob_read_link(&pathBuf, zFullpath));
463
+ md5sum_step_text(zBuf, -1);
464
+ md5sum_step_text(blob_str(&pathBuf), -1);
465
+ blob_reset(&pathBuf);
466
+ }else{
467
+ in = fossil_fopen(zFullpath,"rb");
468
+ if( in==0 ){
469
+ md5sum_step_text(" 0\n", -1);
470
+ continue;
471
+ }
472
+ fseek(in, 0L, SEEK_END);
473
+ sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
474
+ fseek(in, 0L, SEEK_SET);
475
+ md5sum_step_text(zBuf, -1);
476
+ /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
477
+ for(;;){
478
+ int n;
479
+ n = fread(zBuf, 1, sizeof(zBuf), in);
480
+ if( n<=0 ) break;
481
+ md5sum_step_text(zBuf, n);
482
+ }
483
+ fclose(in);
484
+ }
461485
}else{
462486
int rid = db_column_int(&q, 4);
463487
const char *zOrigName = db_column_text(&q, 2);
464488
char zBuf[100];
465489
Blob file;
@@ -500,11 +524,15 @@
500524
const char *zFullpath = db_column_text(&q, 0);
501525
const char *zName = db_column_text(&q, 1);
502526
int rid = db_column_int(&q, 2);
503527
504528
blob_zero(&disk);
505
- rc = blob_read_from_file(&disk, zFullpath);
529
+ if( file_islink(zFullpath) ){
530
+ rc = blob_read_link(&disk, zFullpath);
531
+ }else{
532
+ rc = blob_read_from_file(&disk, zFullpath);
533
+ }
506534
if( rc<0 ){
507535
fossil_print("ERROR: cannot read file [%s]\n", zFullpath);
508536
blob_reset(&disk);
509537
continue;
510538
}
511539
--- src/vfile.c
+++ src/vfile.c
@@ -90,12 +90,12 @@
90 db_begin_transaction();
91 p = manifest_get(vid, CFTYPE_MANIFEST);
92 if( p==0 ) return;
93 db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
94 db_prepare(&ins,
95 "INSERT INTO vfile(vid,isexe,rid,mrid,pathname) "
96 " VALUES(:vid,:isexe,:id,:id,:name)");
97 db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid");
98 db_bind_int(&ins, ":vid", vid);
99 manifest_file_rewind(p);
100 while( (pFile = manifest_file_next(p,0))!=0 ){
101 if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
@@ -110,13 +110,14 @@
110 db_reset(&ridq);
111 if( rid==0 || size<0 ){
112 fossil_warning("content missing for %s", pFile->zName);
113 continue;
114 }
115 db_bind_int(&ins, ":isexe", manifest_file_mperm(pFile));
116 db_bind_int(&ins, ":id", rid);
117 db_bind_text(&ins, ":name", pFile->zName);
 
118 db_step(&ins);
119 db_reset(&ins);
120 }
121 db_finalize(&ridq);
122 db_finalize(&ins);
@@ -165,11 +166,11 @@
165 isDeleted = db_column_int(&q, 3);
166 oldChnged = db_column_int(&q, 4);
167 oldMtime = db_column_int64(&q, 7);
168 if( isDeleted ){
169 chnged = 1;
170 }else if( !file_isfile(zName) && file_size(0)>=0 ){
171 if( notFileIsFatal ){
172 fossil_warning("not an ordinary file: %s", zName);
173 nErr++;
174 }
175 chnged = 1;
@@ -223,29 +224,30 @@
223 Stmt q;
224 Blob content;
225 int nRepos = strlen(g.zLocalRoot);
226
227 if( vid>0 && id==0 ){
228 db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
229 " FROM vfile"
230 " WHERE vid=%d AND mrid>0",
231 g.zLocalRoot, vid);
232 }else{
233 assert( vid==0 && id>0 );
234 db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe"
235 " FROM vfile"
236 " WHERE id=%d AND mrid>0",
237 g.zLocalRoot, id);
238 }
239 while( db_step(&q)==SQLITE_ROW ){
240 int id, rid, isExe;
241 const char *zName;
242
243 id = db_column_int(&q, 0);
244 zName = db_column_text(&q, 1);
245 rid = db_column_int(&q, 2);
246 isExe = db_column_int(&q, 3);
 
247 content_get(rid, &content);
248 if( file_is_the_same(&content, zName) ){
249 blob_reset(&content);
250 if( file_setexe(zName, isExe) ){
251 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
@@ -270,11 +272,22 @@
270 blob_reset(&content);
271 continue;
272 }
273 }
274 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
275 blob_write_to_file(&content, zName);
 
 
 
 
 
 
 
 
 
 
 
276 file_setexe(zName, isExe);
277 blob_reset(&content);
278 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
279 file_mtime(zName), id);
280 }
@@ -380,11 +393,11 @@
380 /* do nothing */
381 }else if( file_isdir(zPath)==1 ){
382 if( !vfile_top_of_checkout(zPath) ){
383 vfile_scan(pPath, nPrefix, allFlag, pIgnore);
384 }
385 }else if( file_isfile(zPath) ){
386 db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
387 db_step(&ins);
388 db_reset(&ins);
389 }
390 blob_resize(pPath, origSize);
@@ -439,27 +452,38 @@
439 const char *zName = db_column_text(&q, 1);
440 int isSelected = db_column_int(&q, 3);
441
442 if( isSelected ){
443 md5sum_step_text(zName, -1);
444 in = fossil_fopen(zFullpath,"rb");
445 if( in==0 ){
446 md5sum_step_text(" 0\n", -1);
447 continue;
448 }
449 fseek(in, 0L, SEEK_END);
450 sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
451 fseek(in, 0L, SEEK_SET);
452 md5sum_step_text(zBuf, -1);
453 /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
454 for(;;){
455 int n;
456 n = fread(zBuf, 1, sizeof(zBuf), in);
457 if( n<=0 ) break;
458 md5sum_step_text(zBuf, n);
459 }
460 fclose(in);
 
 
 
 
 
 
 
 
 
 
 
461 }else{
462 int rid = db_column_int(&q, 4);
463 const char *zOrigName = db_column_text(&q, 2);
464 char zBuf[100];
465 Blob file;
@@ -500,11 +524,15 @@
500 const char *zFullpath = db_column_text(&q, 0);
501 const char *zName = db_column_text(&q, 1);
502 int rid = db_column_int(&q, 2);
503
504 blob_zero(&disk);
505 rc = blob_read_from_file(&disk, zFullpath);
 
 
 
 
506 if( rc<0 ){
507 fossil_print("ERROR: cannot read file [%s]\n", zFullpath);
508 blob_reset(&disk);
509 continue;
510 }
511
--- src/vfile.c
+++ src/vfile.c
@@ -90,12 +90,12 @@
90 db_begin_transaction();
91 p = manifest_get(vid, CFTYPE_MANIFEST);
92 if( p==0 ) return;
93 db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
94 db_prepare(&ins,
95 "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname) "
96 " VALUES(:vid,:isexe,:islink,:id,:id,:name)");
97 db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid");
98 db_bind_int(&ins, ":vid", vid);
99 manifest_file_rewind(p);
100 while( (pFile = manifest_file_next(p,0))!=0 ){
101 if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
@@ -110,13 +110,14 @@
110 db_reset(&ridq);
111 if( rid==0 || size<0 ){
112 fossil_warning("content missing for %s", pFile->zName);
113 continue;
114 }
115 db_bind_int(&ins, ":isexe", ( manifest_file_mperm(pFile)==PERM_EXE ));
116 db_bind_int(&ins, ":id", rid);
117 db_bind_text(&ins, ":name", pFile->zName);
118 db_bind_int(&ins, ":islink", ( manifest_file_mperm(pFile)==PERM_LNK ));
119 db_step(&ins);
120 db_reset(&ins);
121 }
122 db_finalize(&ridq);
123 db_finalize(&ins);
@@ -165,11 +166,11 @@
166 isDeleted = db_column_int(&q, 3);
167 oldChnged = db_column_int(&q, 4);
168 oldMtime = db_column_int64(&q, 7);
169 if( isDeleted ){
170 chnged = 1;
171 }else if( !file_isfile_or_link(zName) && file_size(0)>=0 ){
172 if( notFileIsFatal ){
173 fossil_warning("not an ordinary file: %s", zName);
174 nErr++;
175 }
176 chnged = 1;
@@ -223,29 +224,30 @@
224 Stmt q;
225 Blob content;
226 int nRepos = strlen(g.zLocalRoot);
227
228 if( vid>0 && id==0 ){
229 db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
230 " FROM vfile"
231 " WHERE vid=%d AND mrid>0",
232 g.zLocalRoot, vid);
233 }else{
234 assert( vid==0 && id>0 );
235 db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
236 " FROM vfile"
237 " WHERE id=%d AND mrid>0",
238 g.zLocalRoot, id);
239 }
240 while( db_step(&q)==SQLITE_ROW ){
241 int id, rid, isExe, isLink;
242 const char *zName;
243
244 id = db_column_int(&q, 0);
245 zName = db_column_text(&q, 1);
246 rid = db_column_int(&q, 2);
247 isExe = db_column_int(&q, 3);
248 isLink = db_column_int(&q, 4);
249 content_get(rid, &content);
250 if( file_is_the_same(&content, zName) ){
251 blob_reset(&content);
252 if( file_setexe(zName, isExe) ){
253 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
@@ -270,11 +272,22 @@
272 blob_reset(&content);
273 continue;
274 }
275 }
276 if( verbose ) fossil_print("%s\n", &zName[nRepos]);
277 if( file_isdir(zName) == 1 ){
278 /*TODO(dchest): remove directories? */
279 fossil_fatal("%s is directory, cannot overwrite\n", zName);
280 }
281 if( file_size(zName)>=0 && (isLink || file_islink(zName)) ){
282 file_delete(zName);
283 }
284 if( isLink ){
285 create_symlink(blob_str(&content), zName);
286 }else{
287 blob_write_to_file(&content, zName);
288 }
289 file_setexe(zName, isExe);
290 blob_reset(&content);
291 db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
292 file_mtime(zName), id);
293 }
@@ -380,11 +393,11 @@
393 /* do nothing */
394 }else if( file_isdir(zPath)==1 ){
395 if( !vfile_top_of_checkout(zPath) ){
396 vfile_scan(pPath, nPrefix, allFlag, pIgnore);
397 }
398 }else if( file_isfile_or_link(zPath) ){
399 db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
400 db_step(&ins);
401 db_reset(&ins);
402 }
403 blob_resize(pPath, origSize);
@@ -439,27 +452,38 @@
452 const char *zName = db_column_text(&q, 1);
453 int isSelected = db_column_int(&q, 3);
454
455 if( isSelected ){
456 md5sum_step_text(zName, -1);
457 if( file_islink(zFullpath) ){
458 /* Instead of file content, use link destination path */
459 Blob pathBuf;
460
461 sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n",
462 blob_read_link(&pathBuf, zFullpath));
463 md5sum_step_text(zBuf, -1);
464 md5sum_step_text(blob_str(&pathBuf), -1);
465 blob_reset(&pathBuf);
466 }else{
467 in = fossil_fopen(zFullpath,"rb");
468 if( in==0 ){
469 md5sum_step_text(" 0\n", -1);
470 continue;
471 }
472 fseek(in, 0L, SEEK_END);
473 sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
474 fseek(in, 0L, SEEK_SET);
475 md5sum_step_text(zBuf, -1);
476 /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
477 for(;;){
478 int n;
479 n = fread(zBuf, 1, sizeof(zBuf), in);
480 if( n<=0 ) break;
481 md5sum_step_text(zBuf, n);
482 }
483 fclose(in);
484 }
485 }else{
486 int rid = db_column_int(&q, 4);
487 const char *zOrigName = db_column_text(&q, 2);
488 char zBuf[100];
489 Blob file;
@@ -500,11 +524,15 @@
524 const char *zFullpath = db_column_text(&q, 0);
525 const char *zName = db_column_text(&q, 1);
526 int rid = db_column_int(&q, 2);
527
528 blob_zero(&disk);
529 if( file_islink(zFullpath) ){
530 rc = blob_read_link(&disk, zFullpath);
531 }else{
532 rc = blob_read_from_file(&disk, zFullpath);
533 }
534 if( rc<0 ){
535 fossil_print("ERROR: cannot read file [%s]\n", zFullpath);
536 blob_reset(&disk);
537 continue;
538 }
539
+7 -3
--- src/zip.c
+++ src/zip.c
@@ -117,11 +117,11 @@
117117
** Append a single file to a growing ZIP archive.
118118
**
119119
** pFile is the file to be appended. zName is the name
120120
** that the file should be saved as.
121121
*/
122
-void zip_add_file(const char *zName, const Blob *pFile, int isExe){
122
+void zip_add_file(const char *zName, const Blob *pFile, int mPerm){
123123
z_stream stream;
124124
int nameLen;
125125
int toOut = 0;
126126
int iStart;
127127
int iCRC = 0;
@@ -139,11 +139,15 @@
139139
/* Fill in as much of the header as we know.
140140
*/
141141
nBlob = pFile ? blob_size(pFile) : 0;
142142
if( nBlob>0 ){
143143
iMethod = 8;
144
- iMode = isExe ? 0100755 : 0100644;
144
+ switch( mPerm ){
145
+ case PERM_LNK: iMode = 0120755; break;
146
+ case PERM_EXE: iMode = 0100755; break;
147
+ default: iMode = 0100644; break;
148
+ }
145149
}else{
146150
iMethod = 0;
147151
iMode = 040755;
148152
}
149153
nameLen = strlen(zName);
@@ -287,11 +291,11 @@
287291
}
288292
zip_open();
289293
for(i=3; i<g.argc; i++){
290294
blob_zero(&file);
291295
blob_read_from_file(&file, g.argv[i]);
292
- zip_add_file(g.argv[i], &file, file_isexe(g.argv[i]));
296
+ zip_add_file(g.argv[i], &file, file_perm(g.argv[i]));
293297
blob_reset(&file);
294298
}
295299
zip_close(&zip);
296300
blob_write_to_file(&zip, g.argv[2]);
297301
}
298302
--- src/zip.c
+++ src/zip.c
@@ -117,11 +117,11 @@
117 ** Append a single file to a growing ZIP archive.
118 **
119 ** pFile is the file to be appended. zName is the name
120 ** that the file should be saved as.
121 */
122 void zip_add_file(const char *zName, const Blob *pFile, int isExe){
123 z_stream stream;
124 int nameLen;
125 int toOut = 0;
126 int iStart;
127 int iCRC = 0;
@@ -139,11 +139,15 @@
139 /* Fill in as much of the header as we know.
140 */
141 nBlob = pFile ? blob_size(pFile) : 0;
142 if( nBlob>0 ){
143 iMethod = 8;
144 iMode = isExe ? 0100755 : 0100644;
 
 
 
 
145 }else{
146 iMethod = 0;
147 iMode = 040755;
148 }
149 nameLen = strlen(zName);
@@ -287,11 +291,11 @@
287 }
288 zip_open();
289 for(i=3; i<g.argc; i++){
290 blob_zero(&file);
291 blob_read_from_file(&file, g.argv[i]);
292 zip_add_file(g.argv[i], &file, file_isexe(g.argv[i]));
293 blob_reset(&file);
294 }
295 zip_close(&zip);
296 blob_write_to_file(&zip, g.argv[2]);
297 }
298
--- src/zip.c
+++ src/zip.c
@@ -117,11 +117,11 @@
117 ** Append a single file to a growing ZIP archive.
118 **
119 ** pFile is the file to be appended. zName is the name
120 ** that the file should be saved as.
121 */
122 void zip_add_file(const char *zName, const Blob *pFile, int mPerm){
123 z_stream stream;
124 int nameLen;
125 int toOut = 0;
126 int iStart;
127 int iCRC = 0;
@@ -139,11 +139,15 @@
139 /* Fill in as much of the header as we know.
140 */
141 nBlob = pFile ? blob_size(pFile) : 0;
142 if( nBlob>0 ){
143 iMethod = 8;
144 switch( mPerm ){
145 case PERM_LNK: iMode = 0120755; break;
146 case PERM_EXE: iMode = 0100755; break;
147 default: iMode = 0100644; break;
148 }
149 }else{
150 iMethod = 0;
151 iMode = 040755;
152 }
153 nameLen = strlen(zName);
@@ -287,11 +291,11 @@
291 }
292 zip_open();
293 for(i=3; i<g.argc; i++){
294 blob_zero(&file);
295 blob_read_from_file(&file, g.argv[i]);
296 zip_add_file(g.argv[i], &file, file_perm(g.argv[i]));
297 blob_reset(&file);
298 }
299 zip_close(&zip);
300 blob_write_to_file(&zip, g.argv[2]);
301 }
302

Keyboard Shortcuts

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