Fossil SCM
Merge the symlinks branch into trunk.
Commit
e4f1c1fe950425bb56d8068a12be5a446df46554
Parent
8b9c933c283a472…
25 files changed
-9
-9
+4
-4
+26
+18
-5
+2
-1
+49
-32
+49
-32
+32
-10
+9
-2
+122
-6
+1
-1
+12
+14
-6
+2
+14
-4
+50
-30
+1
+11
+84
-33
+21
-4
+30
-9
+61
-28
+55
-27
+7
-3
-
Makefile.in
-
Makefile.in
~
src/add.c
~
src/blob.c
~
src/checkin.c
~
src/checkout.c
~
src/db.c
~
src/db.c
~
src/diffcmd.c
~
src/export.c
~
src/file.c
~
src/finfo.c
~
src/import.c
~
src/info.c
~
src/main.c
~
src/manifest.c
~
src/merge.c
~
src/schema.c
~
src/sha1.c
~
src/stash.c
~
src/tar.c
~
src/undo.c
~
src/update.c
~
src/vfile.c
~
src/zip.c
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 @@ | ||
| 106 | 106 | db_multi_exec("UPDATE vfile SET deleted=0" |
| 107 | 107 | " WHERE pathname=%Q COLLATE %s", zPath, zCollate); |
| 108 | 108 | }else{ |
| 109 | 109 | char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); |
| 110 | 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)); | |
| 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 | 114 | fossil_free(zFullname); |
| 115 | 115 | } |
| 116 | 116 | if( db_changes() ){ |
| 117 | 117 | fossil_print("ADDED %s\n", zPath); |
| 118 | 118 | return 1; |
| @@ -426,11 +426,11 @@ | ||
| 426 | 426 | const char * zFile; |
| 427 | 427 | const char * zPath; |
| 428 | 428 | |
| 429 | 429 | zFile = db_column_text(&q, 0); |
| 430 | 430 | zPath = db_column_text(&q, 1); |
| 431 | - if( !file_isfile(zPath) ){ | |
| 431 | + if( !file_isfile_or_link(zPath) ){ | |
| 432 | 432 | if( !isTest ){ |
| 433 | 433 | db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile); |
| 434 | 434 | } |
| 435 | 435 | fossil_print("DELETED %s\n", zFile); |
| 436 | 436 | nDelete++; |
| 437 | 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)" |
| 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 @@ | ||
| 701 | 701 | if( got<size ){ |
| 702 | 702 | blob_resize(pBlob, got); |
| 703 | 703 | } |
| 704 | 704 | return got; |
| 705 | 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 | + | |
| 706 | 732 | |
| 707 | 733 | /* |
| 708 | 734 | ** Write the content of a blob into a file. |
| 709 | 735 | ** |
| 710 | 736 | ** If the filename is blank or "-" then write to standard output. |
| 711 | 737 |
| --- 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 @@ | ||
| 64 | 64 | } |
| 65 | 65 | } |
| 66 | 66 | blob_append(report, zPrefix, nPrefix); |
| 67 | 67 | if( isDeleted ){ |
| 68 | 68 | blob_appendf(report, "DELETED %s\n", zDisplayName); |
| 69 | - }else if( !file_isfile(zFullName) ){ | |
| 69 | + }else if( !file_isfile_or_link(zFullName) ){ | |
| 70 | 70 | if( file_access(zFullName, 0)==0 ){ |
| 71 | 71 | blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName); |
| 72 | 72 | if( missingIsFatal ){ |
| 73 | 73 | fossil_warning("not a file: %s", zDisplayName); |
| 74 | 74 | nErr++; |
| @@ -227,11 +227,11 @@ | ||
| 227 | 227 | fossil_print("%s\n", zPathname); |
| 228 | 228 | }else if( isNew ){ |
| 229 | 229 | fossil_print("ADDED %s\n", zPathname); |
| 230 | 230 | }else if( isDeleted ){ |
| 231 | 231 | fossil_print("DELETED %s\n", zPathname); |
| 232 | - }else if( !file_isfile(zFullName) ){ | |
| 232 | + }else if( !file_isfile_or_link(zFullName) ){ | |
| 233 | 233 | if( file_access(zFullName, 0)==0 ){ |
| 234 | 234 | fossil_print("NOT_A_FILE %s\n", zPathname); |
| 235 | 235 | }else{ |
| 236 | 236 | fossil_print("MISSING %s\n", zPathname); |
| 237 | 237 | } |
| @@ -635,11 +635,11 @@ | ||
| 635 | 635 | blob_appendf(pOut, "C %F\n", blob_str(pComment)); |
| 636 | 636 | zDate = date_in_standard_format(zDateOvrd ? zDateOvrd : "now"); |
| 637 | 637 | blob_appendf(pOut, "D %s\n", zDate); |
| 638 | 638 | zDate[10] = ' '; |
| 639 | 639 | db_prepare(&q, |
| 640 | - "SELECT pathname, uuid, origname, blob.rid, isexe," | |
| 640 | + "SELECT pathname, uuid, origname, blob.rid, isexe, islink," | |
| 641 | 641 | " file_is_selected(vfile.id)" |
| 642 | 642 | " FROM vfile JOIN blob ON vfile.mrid=blob.rid" |
| 643 | 643 | " WHERE (NOT deleted OR NOT file_is_selected(vfile.id))" |
| 644 | 644 | " AND vfile.vid=%d" |
| 645 | 645 | " ORDER BY 1", vid); |
| @@ -650,11 +650,12 @@ | ||
| 650 | 650 | const char *zName = db_column_text(&q, 0); |
| 651 | 651 | const char *zUuid = db_column_text(&q, 1); |
| 652 | 652 | const char *zOrig = db_column_text(&q, 2); |
| 653 | 653 | int frid = db_column_int(&q, 3); |
| 654 | 654 | 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); | |
| 656 | 657 | const char *zPerm; |
| 657 | 658 | int cmp; |
| 658 | 659 | #if !defined(_WIN32) |
| 659 | 660 | /* For unix, extract the "executable" permission bit directly from |
| 660 | 661 | ** the filesystem. On windows, the "executable" bit is retained |
| @@ -661,13 +662,20 @@ | ||
| 661 | 662 | ** unchanged from the original. |
| 662 | 663 | */ |
| 663 | 664 | blob_resize(&filename, nBasename); |
| 664 | 665 | blob_append(&filename, zName, -1); |
| 665 | 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)); | |
| 666 | 672 | #endif |
| 667 | 673 | if( isexe ){ |
| 668 | 674 | zPerm = " x"; |
| 675 | + }else if( isLink ){ | |
| 676 | + zPerm = " l"; /* note: symlinks don't have executable bit on unix */ | |
| 669 | 677 | }else{ |
| 670 | 678 | zPerm = ""; |
| 671 | 679 | } |
| 672 | 680 | if( !g.markPrivate ) content_make_public(frid); |
| 673 | 681 | while( pFile && fossil_strcmp(pFile->zName,zName)<0 ){ |
| @@ -1063,11 +1071,16 @@ | ||
| 1063 | 1071 | zFullname = db_column_text(&q, 1); |
| 1064 | 1072 | rid = db_column_int(&q, 2); |
| 1065 | 1073 | crnlOk = db_column_int(&q, 3); |
| 1066 | 1074 | |
| 1067 | 1075 | 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 | + } | |
| 1069 | 1082 | if( !crnlOk ) cr_warning(&content, zFullname); |
| 1070 | 1083 | nrid = content_put(&content); |
| 1071 | 1084 | blob_reset(&content); |
| 1072 | 1085 | if( rid>0 ){ |
| 1073 | 1086 | content_deltify(rid, nrid, 0); |
| 1074 | 1087 |
| --- 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 @@ | ||
| 92 | 92 | db_reset(&s); |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | 95 | /* |
| 96 | 96 | ** 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. | |
| 98 | 99 | */ |
| 99 | 100 | void checkout_set_all_exe(int vid){ |
| 100 | 101 | Blob filename; |
| 101 | 102 | int baseLen; |
| 102 | 103 | Manifest *pManifest; |
| 103 | 104 |
| --- 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 |
M
src/db.c
+49
-32
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -736,18 +736,35 @@ | ||
| 736 | 736 | } |
| 737 | 737 | g.configOpen = 1; |
| 738 | 738 | free(zDbName); |
| 739 | 739 | } |
| 740 | 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 | + | |
| 741 | 760 | /* |
| 742 | 761 | ** If zDbName is a valid local database file, open it and return |
| 743 | 762 | ** true. If it is not a valid local database file, return 0. |
| 744 | 763 | */ |
| 745 | 764 | static int isValidLocalDb(const char *zDbName){ |
| 746 | 765 | i64 lsize; |
| 747 | - int rc; | |
| 748 | - sqlite3_stmt *pStmt; | |
| 749 | 766 | |
| 750 | 767 | if( file_access(zDbName, F_OK) ) return 0; |
| 751 | 768 | lsize = file_size(zDbName); |
| 752 | 769 | if( lsize%1024!=0 || lsize<4096 ) return 0; |
| 753 | 770 | db_open_or_attach(zDbName, "localdb"); |
| @@ -757,40 +774,31 @@ | ||
| 757 | 774 | |
| 758 | 775 | /* If the "isexe" column is missing from the vfile table, then |
| 759 | 776 | ** add it now. This code added on 2010-03-06. After all users have |
| 760 | 777 | ** upgraded, this code can be safely deleted. |
| 761 | 778 | */ |
| 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"); | |
| 792 | 800 | |
| 793 | 801 | return 1; |
| 794 | 802 | } |
| 795 | 803 | |
| 796 | 804 | /* |
| @@ -866,10 +874,12 @@ | ||
| 866 | 874 | } |
| 867 | 875 | } |
| 868 | 876 | db_open_or_attach(zDbName, "repository"); |
| 869 | 877 | g.repositoryOpen = 1; |
| 870 | 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); | |
| 871 | 881 | } |
| 872 | 882 | |
| 873 | 883 | /* |
| 874 | 884 | ** Flags for the db_find_and_open_repository() function. |
| 875 | 885 | */ |
| @@ -1747,10 +1757,11 @@ | ||
| 1747 | 1757 | char const *def; /* Default value */ |
| 1748 | 1758 | }; |
| 1749 | 1759 | #endif /* INTERFACE */ |
| 1750 | 1760 | struct stControlSettings const ctrlSettings[] = { |
| 1751 | 1761 | { "access-log", 0, 0, 0, "off" }, |
| 1762 | + { "allow-symlinks",0, 0, 0, "off" }, | |
| 1752 | 1763 | { "auto-captcha", "autocaptcha", 0, 0, "on" }, |
| 1753 | 1764 | { "auto-shun", 0, 0, 0, "on" }, |
| 1754 | 1765 | { "autosync", 0, 0, 0, "on" }, |
| 1755 | 1766 | { "binary-glob", 0, 32, 1, "" }, |
| 1756 | 1767 | { "clearsign", 0, 0, 0, "off" }, |
| @@ -1799,10 +1810,16 @@ | ||
| 1799 | 1810 | ** file named .fossil-settings/PROPERTY in the checked out files, if that |
| 1800 | 1811 | ** file exists. |
| 1801 | 1812 | ** |
| 1802 | 1813 | ** The "unset" command clears a property setting. |
| 1803 | 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 | |
| 1804 | 1821 | ** |
| 1805 | 1822 | ** auto-captcha If enabled, the Login page provides a button to |
| 1806 | 1823 | ** fill in the captcha password. Default: on |
| 1807 | 1824 | ** |
| 1808 | 1825 | ** auto-shun If enabled, automatically pull the shunning list |
| 1809 | 1826 |
| --- 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 |
M
src/db.c
+49
-32
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -736,18 +736,35 @@ | ||
| 736 | 736 | } |
| 737 | 737 | g.configOpen = 1; |
| 738 | 738 | free(zDbName); |
| 739 | 739 | } |
| 740 | 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 | + | |
| 741 | 760 | /* |
| 742 | 761 | ** If zDbName is a valid local database file, open it and return |
| 743 | 762 | ** true. If it is not a valid local database file, return 0. |
| 744 | 763 | */ |
| 745 | 764 | static int isValidLocalDb(const char *zDbName){ |
| 746 | 765 | i64 lsize; |
| 747 | - int rc; | |
| 748 | - sqlite3_stmt *pStmt; | |
| 749 | 766 | |
| 750 | 767 | if( file_access(zDbName, F_OK) ) return 0; |
| 751 | 768 | lsize = file_size(zDbName); |
| 752 | 769 | if( lsize%1024!=0 || lsize<4096 ) return 0; |
| 753 | 770 | db_open_or_attach(zDbName, "localdb"); |
| @@ -757,40 +774,31 @@ | ||
| 757 | 774 | |
| 758 | 775 | /* If the "isexe" column is missing from the vfile table, then |
| 759 | 776 | ** add it now. This code added on 2010-03-06. After all users have |
| 760 | 777 | ** upgraded, this code can be safely deleted. |
| 761 | 778 | */ |
| 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"); | |
| 792 | 800 | |
| 793 | 801 | return 1; |
| 794 | 802 | } |
| 795 | 803 | |
| 796 | 804 | /* |
| @@ -866,10 +874,12 @@ | ||
| 866 | 874 | } |
| 867 | 875 | } |
| 868 | 876 | db_open_or_attach(zDbName, "repository"); |
| 869 | 877 | g.repositoryOpen = 1; |
| 870 | 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); | |
| 871 | 881 | } |
| 872 | 882 | |
| 873 | 883 | /* |
| 874 | 884 | ** Flags for the db_find_and_open_repository() function. |
| 875 | 885 | */ |
| @@ -1747,10 +1757,11 @@ | ||
| 1747 | 1757 | char const *def; /* Default value */ |
| 1748 | 1758 | }; |
| 1749 | 1759 | #endif /* INTERFACE */ |
| 1750 | 1760 | struct stControlSettings const ctrlSettings[] = { |
| 1751 | 1761 | { "access-log", 0, 0, 0, "off" }, |
| 1762 | + { "allow-symlinks",0, 0, 0, "off" }, | |
| 1752 | 1763 | { "auto-captcha", "autocaptcha", 0, 0, "on" }, |
| 1753 | 1764 | { "auto-shun", 0, 0, 0, "on" }, |
| 1754 | 1765 | { "autosync", 0, 0, 0, "on" }, |
| 1755 | 1766 | { "binary-glob", 0, 32, 1, "" }, |
| 1756 | 1767 | { "clearsign", 0, 0, 0, "off" }, |
| @@ -1799,10 +1810,16 @@ | ||
| 1799 | 1810 | ** file named .fossil-settings/PROPERTY in the checked out files, if that |
| 1800 | 1811 | ** file exists. |
| 1801 | 1812 | ** |
| 1802 | 1813 | ** The "unset" command clears a property setting. |
| 1803 | 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 | |
| 1804 | 1821 | ** |
| 1805 | 1822 | ** auto-captcha If enabled, the Login page provides a button to |
| 1806 | 1823 | ** fill in the captcha password. Default: on |
| 1807 | 1824 | ** |
| 1808 | 1825 | ** auto-shun If enabled, automatically pull the shunning list |
| 1809 | 1826 |
| --- 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 @@ | ||
| 74 | 74 | /* Read content of zFile2 into memory */ |
| 75 | 75 | blob_zero(&file2); |
| 76 | 76 | if( file_size(zFile2)<0 ){ |
| 77 | 77 | zName2 = "/dev/null"; |
| 78 | 78 | }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 | + } | |
| 80 | 84 | zName2 = zName; |
| 81 | 85 | } |
| 82 | 86 | |
| 83 | 87 | /* Compute and output the differences */ |
| 84 | 88 | blob_zero(&out); |
| @@ -186,13 +190,18 @@ | ||
| 186 | 190 | int ignoreEolWs, /* Ignore whitespace changes at end of lines */ |
| 187 | 191 | const char *zFileTreeName |
| 188 | 192 | ){ |
| 189 | 193 | Blob fname; |
| 190 | 194 | Blob content; |
| 195 | + int isLink; | |
| 191 | 196 | 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 | + } | |
| 194 | 203 | blob_reset(&content); |
| 195 | 204 | blob_reset(&fname); |
| 196 | 205 | } |
| 197 | 206 | |
| 198 | 207 | /* |
| @@ -222,32 +231,32 @@ | ||
| 222 | 231 | if( !is_a_version(rid) ){ |
| 223 | 232 | fossil_fatal("no such check-in: %s", zFrom); |
| 224 | 233 | } |
| 225 | 234 | load_vfile_from_rid(rid); |
| 226 | 235 | 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" | |
| 228 | 237 | " FROM vfile v1, vfile v2 " |
| 229 | 238 | " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d" |
| 230 | 239 | " AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)" |
| 231 | 240 | "UNION " |
| 232 | - "SELECT pathname, 1, 0, 0, 0" | |
| 241 | + "SELECT pathname, 1, 0, 0, 0, islink" | |
| 233 | 242 | " FROM vfile v1" |
| 234 | 243 | " WHERE v1.vid=%d" |
| 235 | 244 | " AND NOT EXISTS(SELECT 1 FROM vfile v2" |
| 236 | 245 | " WHERE v2.vid=%d AND v2.pathname=v1.pathname)" |
| 237 | 246 | "UNION " |
| 238 | - "SELECT pathname, 0, 0, 1, 0" | |
| 247 | + "SELECT pathname, 0, 0, 1, 0, islink" | |
| 239 | 248 | " FROM vfile v2" |
| 240 | 249 | " WHERE v2.vid=%d" |
| 241 | 250 | " AND NOT EXISTS(SELECT 1 FROM vfile v1" |
| 242 | 251 | " WHERE v1.vid=%d AND v1.pathname=v2.pathname)" |
| 243 | 252 | " ORDER BY 1", |
| 244 | 253 | rid, vid, rid, vid, vid, rid |
| 245 | 254 | ); |
| 246 | 255 | }else{ |
| 247 | 256 | blob_appendf(&sql, |
| 248 | - "SELECT pathname, deleted, chnged , rid==0, rid" | |
| 257 | + "SELECT pathname, deleted, chnged , rid==0, rid, islink" | |
| 249 | 258 | " FROM vfile" |
| 250 | 259 | " WHERE vid=%d" |
| 251 | 260 | " AND (deleted OR chnged OR rid==0)" |
| 252 | 261 | " ORDER BY pathname", |
| 253 | 262 | vid |
| @@ -258,10 +267,11 @@ | ||
| 258 | 267 | const char *zPathname = db_column_text(&q,0); |
| 259 | 268 | int isDeleted = db_column_int(&q, 1); |
| 260 | 269 | int isChnged = db_column_int(&q,2); |
| 261 | 270 | int isNew = db_column_int(&q,3); |
| 262 | 271 | int srcid = db_column_int(&q, 4); |
| 272 | + int isLink = db_column_int(&q, 5); | |
| 263 | 273 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 264 | 274 | char *zToFree = zFullName; |
| 265 | 275 | int showDiff = 1; |
| 266 | 276 | if( isDeleted ){ |
| 267 | 277 | diff_printf("DELETED %s\n", zPathname); |
| @@ -278,10 +288,16 @@ | ||
| 278 | 288 | srcid = 0; |
| 279 | 289 | if( !asNewFile ){ showDiff = 0; } |
| 280 | 290 | } |
| 281 | 291 | if( showDiff ){ |
| 282 | 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 | + } | |
| 283 | 299 | if( srcid>0 ){ |
| 284 | 300 | content_get(srcid, &content); |
| 285 | 301 | }else{ |
| 286 | 302 | blob_zero(&content); |
| 287 | 303 | } |
| @@ -307,15 +323,21 @@ | ||
| 307 | 323 | const char *zFileTreeName |
| 308 | 324 | ){ |
| 309 | 325 | char *zName; |
| 310 | 326 | Blob fname; |
| 311 | 327 | Blob v1, v2; |
| 328 | + int isLink1, isLink2; | |
| 312 | 329 | file_tree_name(zFileTreeName, &fname, 1); |
| 313 | 330 | 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 | + } | |
| 317 | 339 | blob_reset(&v1); |
| 318 | 340 | blob_reset(&v2); |
| 319 | 341 | blob_reset(&fname); |
| 320 | 342 | } |
| 321 | 343 | |
| 322 | 344 |
| --- 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 @@ | ||
| 269 | 269 | const char *zName = db_column_text(&q4,0); |
| 270 | 270 | int zNew = db_column_int(&q4,1); |
| 271 | 271 | int mPerm = db_column_int(&q4,2); |
| 272 | 272 | if( zNew==0) |
| 273 | 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); | |
| 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 | + } | |
| 276 | 283 | } |
| 277 | 284 | db_finalize(&q4); |
| 278 | 285 | db_finalize(&q3); |
| 279 | 286 | printf("\n"); |
| 280 | 287 | } |
| 281 | 288 |
| --- 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 @@ | ||
| 30 | 30 | ** |
| 31 | 31 | ** Use _stati64 rather than stat on windows, in order to handle files |
| 32 | 32 | ** larger than 2GB. |
| 33 | 33 | */ |
| 34 | 34 | #if defined(_WIN32) && defined(__MSVCRT__) |
| 35 | - static struct _stati64 fileStat; | |
| 36 | 35 | # define stat _stati64 |
| 37 | -#else | |
| 38 | - static struct stat fileStat; | |
| 39 | 36 | #endif |
| 40 | 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 | +} | |
| 41 | 51 | |
| 42 | 52 | /* |
| 43 | 53 | ** Fill in the fileStat variable for the file named zFilename. |
| 44 | 54 | ** If zFilename==0, then use the previous value of fileStat if |
| 45 | 55 | ** there is a previous value. |
| @@ -50,11 +60,11 @@ | ||
| 50 | 60 | int rc = 0; |
| 51 | 61 | if( zFilename==0 ){ |
| 52 | 62 | if( fileStatValid==0 ) rc = 1; |
| 53 | 63 | }else{ |
| 54 | 64 | char *zMbcs = fossil_utf8_to_mbcs(zFilename); |
| 55 | - if( stat(zMbcs, &fileStat)!=0 ){ | |
| 65 | + if( fossil_stat(zMbcs, &fileStat)!=0 ){ | |
| 56 | 66 | fileStatValid = 0; |
| 57 | 67 | rc = 1; |
| 58 | 68 | }else{ |
| 59 | 69 | fileStatValid = 1; |
| 60 | 70 | rc = 0; |
| @@ -80,18 +90,98 @@ | ||
| 80 | 90 | ** recently stat-ed file. |
| 81 | 91 | */ |
| 82 | 92 | i64 file_mtime(const char *zFilename){ |
| 83 | 93 | return getStat(zFilename) ? -1 : fileStat.st_mtime; |
| 84 | 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 | +} | |
| 85 | 112 | |
| 86 | 113 | /* |
| 87 | 114 | ** Return TRUE if the named file is an ordinary file. Return false |
| 88 | 115 | ** for directories, devices, fifos, symlinks, etc. |
| 89 | 116 | */ |
| 90 | 117 | int file_isfile(const char *zFilename){ |
| 91 | 118 | return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode); |
| 92 | 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 | +} | |
| 93 | 183 | |
| 94 | 184 | /* |
| 95 | 185 | ** Return TRUE if the named file is an executable. Return false |
| 96 | 186 | ** for directories, devices, fifos, symlinks, etc. |
| 97 | 187 | */ |
| @@ -105,10 +195,22 @@ | ||
| 105 | 195 | #else |
| 106 | 196 | return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0; |
| 107 | 197 | #endif |
| 108 | 198 | } |
| 109 | 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 | +} | |
| 110 | 212 | |
| 111 | 213 | /* |
| 112 | 214 | ** Return 1 if zFilename is a directory. Return 0 if zFilename |
| 113 | 215 | ** does not exist. Return 2 if zFilename exists but is something |
| 114 | 216 | ** other than a directory. |
| @@ -122,11 +224,19 @@ | ||
| 122 | 224 | rc = getStat(zFN); |
| 123 | 225 | free(zFN); |
| 124 | 226 | }else{ |
| 125 | 227 | rc = getStat(0); |
| 126 | 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 | |
| 127 | 236 | return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2); |
| 237 | +#endif | |
| 128 | 238 | } |
| 129 | 239 | |
| 130 | 240 | /* |
| 131 | 241 | ** Wrapper around the access() system call. |
| 132 | 242 | */ |
| @@ -199,11 +309,11 @@ | ||
| 199 | 309 | */ |
| 200 | 310 | int file_setexe(const char *zFilename, int onoff){ |
| 201 | 311 | int rc = 0; |
| 202 | 312 | #if !defined(_WIN32) |
| 203 | 313 | 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; | |
| 205 | 315 | if( onoff ){ |
| 206 | 316 | int targetMode = (buf.st_mode & 0444)>>2; |
| 207 | 317 | if( (buf.st_mode & 0111)!=targetMode ){ |
| 208 | 318 | chmod(zFilename, buf.st_mode | targetMode); |
| 209 | 319 | rc = 1; |
| @@ -469,10 +579,12 @@ | ||
| 469 | 579 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zName)); |
| 470 | 580 | fossil_print(" file_size = %s\n", zBuf); |
| 471 | 581 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_mtime(zName)); |
| 472 | 582 | fossil_print(" file_mtime = %s\n", zBuf); |
| 473 | 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)); | |
| 474 | 586 | fossil_print(" file_isexe = %d\n", file_isexe(zName)); |
| 475 | 587 | fossil_print(" file_isdir = %d\n", file_isdir(zName)); |
| 476 | 588 | } |
| 477 | 589 | } |
| 478 | 590 | |
| @@ -739,11 +851,15 @@ | ||
| 739 | 851 | Blob onDisk; |
| 740 | 852 | |
| 741 | 853 | iSize = file_size(zName); |
| 742 | 854 | if( iSize<0 ) return 0; |
| 743 | 855 | 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 | + } | |
| 745 | 861 | rc = blob_compare(&onDisk, pContent); |
| 746 | 862 | blob_reset(&onDisk); |
| 747 | 863 | return rc==0; |
| 748 | 864 | } |
| 749 | 865 | |
| 750 | 866 |
| --- 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 @@ | ||
| 99 | 99 | Blob fname; |
| 100 | 100 | const char *zRevision = find_option("revision", "r", 1); |
| 101 | 101 | |
| 102 | 102 | file_tree_name(g.argv[2], &fname, 1); |
| 103 | 103 | 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); | |
| 105 | 105 | }else{ |
| 106 | 106 | int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B", &fname); |
| 107 | 107 | if( rid==0 ){ |
| 108 | 108 | fossil_fatal("no history for file: %b", &fname); |
| 109 | 109 | } |
| 110 | 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); |
| 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 @@ | ||
| 30 | 30 | char *zName; /* Name of a file */ |
| 31 | 31 | char *zUuid; /* UUID of the file */ |
| 32 | 32 | char *zPrior; /* Prior name if the name was changed */ |
| 33 | 33 | char isFrom; /* True if obtained from the parent */ |
| 34 | 34 | char isExe; /* True if executable */ |
| 35 | + char isLink; /* True if symlink */ | |
| 35 | 36 | }; |
| 36 | 37 | #endif |
| 37 | 38 | |
| 38 | 39 | |
| 39 | 40 | /* |
| @@ -58,10 +59,11 @@ | ||
| 58 | 59 | char **azMerge; /* Merge values */ |
| 59 | 60 | int nFile; /* Number of aFile values */ |
| 60 | 61 | int nFileAlloc; /* Number of slots in aFile[] */ |
| 61 | 62 | ImportFile *aFile; /* Information about files in a commit */ |
| 62 | 63 | int fromLoaded; /* True zFrom content loaded into aFile[] */ |
| 64 | + int hasLinks; /* True if git repository contains symlinks */ | |
| 63 | 65 | int tagCommit; /* True if the commit adds a tag */ |
| 64 | 66 | } gg; |
| 65 | 67 | |
| 66 | 68 | /* |
| 67 | 69 | ** Duplicate a string. |
| @@ -242,10 +244,13 @@ | ||
| 242 | 244 | const char *zUuid = gg.aFile[i].zUuid; |
| 243 | 245 | if( zUuid==0 ) continue; |
| 244 | 246 | blob_appendf(&record, "F %F %s", gg.aFile[i].zName, zUuid); |
| 245 | 247 | if( gg.aFile[i].isExe ){ |
| 246 | 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; | |
| 247 | 252 | }else{ |
| 248 | 253 | blob_append(&record, "\n", 1); |
| 249 | 254 | } |
| 250 | 255 | } |
| 251 | 256 | if( gg.zFrom ){ |
| @@ -421,10 +426,11 @@ | ||
| 421 | 426 | manifest_file_rewind(p); |
| 422 | 427 | while( (pOld = manifest_file_next(p, 0))!=0 ){ |
| 423 | 428 | pNew = import_add_file(); |
| 424 | 429 | pNew->zName = fossil_strdup(pOld->zName); |
| 425 | 430 | pNew->isExe = pOld->zPerm && strstr(pOld->zPerm, "x")!=0; |
| 431 | + pNew->isLink = pOld->zPerm && strstr(pOld->zPerm, "l")!=0; | |
| 426 | 432 | pNew->zUuid = fossil_strdup(pOld->zUuid); |
| 427 | 433 | pNew->isFrom = 1; |
| 428 | 434 | } |
| 429 | 435 | manifest_destroy(p); |
| 430 | 436 | } |
| @@ -597,10 +603,11 @@ | ||
| 597 | 603 | if( pFile==0 ){ |
| 598 | 604 | pFile = import_add_file(); |
| 599 | 605 | pFile->zName = fossil_strdup(zName); |
| 600 | 606 | } |
| 601 | 607 | pFile->isExe = (fossil_strcmp(zPerm, "100755")==0); |
| 608 | + pFile->isLink = (fossil_strcmp(zPerm, "120000")==0); | |
| 602 | 609 | fossil_free(pFile->zUuid); |
| 603 | 610 | pFile->zUuid = resolve_committish(zUuid); |
| 604 | 611 | pFile->isFrom = 0; |
| 605 | 612 | }else |
| 606 | 613 | if( memcmp(zLine, "D ", 2)==0 ){ |
| @@ -634,10 +641,11 @@ | ||
| 634 | 641 | pNew->zName = mprintf("%s%s", zTo, pFile->zName[nFrom]); |
| 635 | 642 | }else{ |
| 636 | 643 | pNew->zName = fossil_strdup(pFile->zName); |
| 637 | 644 | } |
| 638 | 645 | pNew->isExe = pFile->isExe; |
| 646 | + pNew->isLink = pFile->isLink; | |
| 639 | 647 | pNew->zUuid = fossil_strdup(pFile->zUuid); |
| 640 | 648 | pNew->isFrom = 0; |
| 641 | 649 | } |
| 642 | 650 | }else |
| 643 | 651 | if( memcmp(zLine, "R ", 2)==0 ){ |
| @@ -657,10 +665,11 @@ | ||
| 657 | 665 | }else{ |
| 658 | 666 | pNew->zName = fossil_strdup(pFile->zName); |
| 659 | 667 | } |
| 660 | 668 | pNew->zPrior = pFile->zName; |
| 661 | 669 | pNew->isExe = pFile->isExe; |
| 670 | + pNew->isLink = pFile->isLink; | |
| 662 | 671 | pNew->zUuid = pFile->zUuid; |
| 663 | 672 | pNew->isFrom = 0; |
| 664 | 673 | gg.nFile--; |
| 665 | 674 | *pFile = *pNew; |
| 666 | 675 | memset(pNew, 0, sizeof(*pNew)); |
| @@ -677,10 +686,13 @@ | ||
| 677 | 686 | { |
| 678 | 687 | goto malformed_line; |
| 679 | 688 | } |
| 680 | 689 | } |
| 681 | 690 | gg.xFinish(); |
| 691 | + if( gg.hasLinks ){ | |
| 692 | + db_set_int("allow-symlinks", 1, 0); | |
| 693 | + } | |
| 682 | 694 | import_reset(1); |
| 683 | 695 | return; |
| 684 | 696 | |
| 685 | 697 | malformed_line: |
| 686 | 698 | trim_newline(zLine); |
| 687 | 699 |
| --- 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 @@ | ||
| 282 | 282 | const char *zName, /* Name of the file that has changed */ |
| 283 | 283 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| 284 | 284 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 285 | 285 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 286 | 286 | int showDiff, /* Show edit diffs if true */ |
| 287 | - int mperm /* EXE permission for zNew */ | |
| 287 | + int mperm /* executable or symlink permission for zNew */ | |
| 288 | 288 | ){ |
| 289 | 289 | if( !g.okHistory ){ |
| 290 | 290 | if( zNew==0 ){ |
| 291 | 291 | @ <p>Deleted %h(zName)</p> |
| 292 | 292 | }else if( zOld==0 ){ |
| 293 | 293 | @ <p>Added %h(zName)</p> |
| 294 | 294 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 295 | 295 | @ <p>Name change from %h(zOldName) to %h(zName) |
| 296 | 296 | }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> | |
| 298 | 299 | }else{ |
| 299 | 300 | @ <p>Changes to %h(zName)</p> |
| 300 | 301 | } |
| 301 | 302 | if( showDiff ){ |
| 302 | 303 | @ <blockquote><pre> |
| @@ -312,11 +313,11 @@ | ||
| 312 | 313 | }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ |
| 313 | 314 | @ <p>Name change from |
| 314 | 315 | @ from <a href="%s(g.zTop)/finfo?name=%T(zOldName)">%h(zOldName)</a> |
| 315 | 316 | @ to <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>. |
| 316 | 317 | }else{ |
| 317 | - @ <p>Execute permission %s(mperm?"set":"cleared") for | |
| 318 | + @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for | |
| 318 | 319 | @ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 319 | 320 | } |
| 320 | 321 | }else if( zOld ){ |
| 321 | 322 | @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 322 | 323 | @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> |
| @@ -782,11 +783,11 @@ | ||
| 782 | 783 | |
| 783 | 784 | db_prepare(&q, |
| 784 | 785 | "SELECT filename.name, datetime(event.mtime)," |
| 785 | 786 | " coalesce(event.ecomment,event.comment)," |
| 786 | 787 | " coalesce(event.euser,event.user)," |
| 787 | - " b.uuid," | |
| 788 | + " b.uuid, mlink.mperm," | |
| 788 | 789 | " coalesce((SELECT value FROM tagxref" |
| 789 | 790 | " WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk')" |
| 790 | 791 | " FROM mlink, filename, event, blob a, blob b" |
| 791 | 792 | " WHERE filename.fnid=mlink.fnid" |
| 792 | 793 | " AND event.objid=mlink.mid" |
| @@ -801,16 +802,23 @@ | ||
| 801 | 802 | const char *zName = db_column_text(&q, 0); |
| 802 | 803 | const char *zDate = db_column_text(&q, 1); |
| 803 | 804 | const char *zCom = db_column_text(&q, 2); |
| 804 | 805 | const char *zUser = db_column_text(&q, 3); |
| 805 | 806 | 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); | |
| 807 | 809 | if( !prevName || fossil_strcmp(zName, prevName) ) { |
| 808 | 810 | if( prevName ) { |
| 809 | 811 | @ </ul> |
| 810 | 812 | } |
| 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 | + } | |
| 812 | 820 | if( g.okHistory ){ |
| 813 | 821 | @ <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 814 | 822 | }else{ |
| 815 | 823 | @ %h(zName) |
| 816 | 824 | } |
| 817 | 825 |
| --- 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 @@ | ||
| 156 | 156 | const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */ |
| 157 | 157 | char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */ |
| 158 | 158 | const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */ |
| 159 | 159 | const char **azAuxOpt[MX_AUX]; /* Options of each option() value */ |
| 160 | 160 | int anAuxCols[MX_AUX]; /* Number of columns for option() values */ |
| 161 | + | |
| 162 | + int allowSymlinks; /* Cached "allow-symlinks" option */ | |
| 161 | 163 | }; |
| 162 | 164 | |
| 163 | 165 | /* |
| 164 | 166 | ** Macro for debugging: |
| 165 | 167 | */ |
| 166 | 168 |
| --- 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 @@ | ||
| 35 | 35 | #define CFTYPE_WIKI 4 |
| 36 | 36 | #define CFTYPE_TICKET 5 |
| 37 | 37 | #define CFTYPE_ATTACHMENT 6 |
| 38 | 38 | #define CFTYPE_EVENT 7 |
| 39 | 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 | + | |
| 40 | 47 | /* |
| 41 | 48 | ** A single F-card within a manifest |
| 42 | 49 | */ |
| 43 | 50 | struct ManifestFile { |
| 44 | 51 | char *zName; /* Name of a file */ |
| @@ -1085,13 +1092,16 @@ | ||
| 1085 | 1092 | /* |
| 1086 | 1093 | ** Compute an appropriate mlink.mperm integer for the permission string |
| 1087 | 1094 | ** of a file. |
| 1088 | 1095 | */ |
| 1089 | 1096 | 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; | |
| 1093 | 1103 | } |
| 1094 | 1104 | return mperm; |
| 1095 | 1105 | } |
| 1096 | 1106 | |
| 1097 | 1107 | /* |
| @@ -1103,11 +1113,11 @@ | ||
| 1103 | 1113 | const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */ |
| 1104 | 1114 | const char *zToUuid, /* UUID for the mlink.fid. "" to delele */ |
| 1105 | 1115 | const char *zFilename, /* Filename */ |
| 1106 | 1116 | const char *zPrior, /* Previous filename. NULL if unchanged */ |
| 1107 | 1117 | int isPublic, /* True if mid is not a private manifest */ |
| 1108 | - int mperm /* 1: exec */ | |
| 1118 | + int mperm /* 1: exec, 2: symlink */ | |
| 1109 | 1119 | ){ |
| 1110 | 1120 | int fnid, pfnid, pid, fid; |
| 1111 | 1121 | static Stmt s1; |
| 1112 | 1122 | |
| 1113 | 1123 | fnid = filename_to_fnid(zFilename); |
| 1114 | 1124 |
| --- 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 @@ | ||
| 167 | 167 | " ridv INTEGER," /* Record ID for current version */ |
| 168 | 168 | " ridp INTEGER," /* Record ID for pivot */ |
| 169 | 169 | " ridm INTEGER," /* Record ID for merge */ |
| 170 | 170 | " isexe BOOLEAN," /* Execute permission enabled */ |
| 171 | 171 | " 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 */ | |
| 173 | 175 | ");", |
| 174 | 176 | caseSensitive ? "binary" : "nocase" |
| 175 | 177 | ); |
| 176 | 178 | |
| 177 | 179 | /* Add files found in V |
| @@ -317,11 +319,22 @@ | ||
| 317 | 319 | } |
| 318 | 320 | } |
| 319 | 321 | db_finalize(&q); |
| 320 | 322 | |
| 321 | 323 | /* |
| 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. | |
| 323 | 336 | ** Copy the M content over into V. |
| 324 | 337 | */ |
| 325 | 338 | db_prepare(&q, |
| 326 | 339 | "SELECT idv, ridm, fn FROM fv" |
| 327 | 340 | " WHERE idp>0 AND idv>0 AND idm>0" |
| @@ -345,11 +358,11 @@ | ||
| 345 | 358 | |
| 346 | 359 | /* |
| 347 | 360 | ** Do a three-way merge on files that have changes on both P->M and P->V. |
| 348 | 361 | */ |
| 349 | 362 | 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" | |
| 351 | 364 | " WHERE idp>0 AND idv>0 AND idm>0" |
| 352 | 365 | " AND ridm!=ridp AND (ridv!=ridp OR chnged)", |
| 353 | 366 | glob_expr("fv.fn", zBinGlob) |
| 354 | 367 | ); |
| 355 | 368 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -358,10 +371,12 @@ | ||
| 358 | 371 | int ridp = db_column_int(&q, 2); |
| 359 | 372 | int ridv = db_column_int(&q, 3); |
| 360 | 373 | int isBinary = db_column_int(&q, 4); |
| 361 | 374 | const char *zName = db_column_text(&q, 5); |
| 362 | 375 | int isExe = db_column_int(&q, 6); |
| 376 | + int islinkv = db_column_int(&q, 7); | |
| 377 | + int islinkm = db_column_int(&q, 8); | |
| 363 | 378 | int rc; |
| 364 | 379 | char *zFullPath; |
| 365 | 380 | Blob m, p, r; |
| 366 | 381 | /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ |
| 367 | 382 | if( detailFlag ){ |
| @@ -368,37 +383,42 @@ | ||
| 368 | 383 | fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", |
| 369 | 384 | zName, ridp, ridm, ridv); |
| 370 | 385 | }else{ |
| 371 | 386 | fossil_print("MERGE %s\n", zName); |
| 372 | 387 | } |
| 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 | + } | |
| 400 | 420 | db_multi_exec("INSERT OR IGNORE INTO vmerge(id,merge) VALUES(%d,%d)", |
| 401 | 421 | idv,ridm); |
| 402 | 422 | } |
| 403 | 423 | db_finalize(&q); |
| 404 | 424 | |
| 405 | 425 |
| --- 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 |
+1
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -461,10 +461,11 @@ | ||
| 461 | 461 | @ id INTEGER PRIMARY KEY, -- ID of the checked out file |
| 462 | 462 | @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. |
| 463 | 463 | @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add |
| 464 | 464 | @ deleted BOOLEAN DEFAULT 0, -- True if deleted |
| 465 | 465 | @ isexe BOOLEAN, -- True if file should be executable |
| 466 | +@ islink BOOLEAN, -- True if file should be symlink | |
| 466 | 467 | @ rid INTEGER, -- Originally from this repository record |
| 467 | 468 | @ mrid INTEGER, -- Based on this record due to a merge |
| 468 | 469 | @ mtime INTEGER, -- Mtime of file on disk. sec since 1970 |
| 469 | 470 | @ pathname TEXT, -- Full pathname relative to root |
| 470 | 471 | @ origname TEXT, -- Original pathname. NULL if unchanged |
| 471 | 472 |
| --- 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 @@ | ||
| 280 | 280 | int sha1sum_file(const char *zFilename, Blob *pCksum){ |
| 281 | 281 | FILE *in; |
| 282 | 282 | SHA1Context ctx; |
| 283 | 283 | unsigned char zResult[20]; |
| 284 | 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 | + } | |
| 285 | 296 | |
| 286 | 297 | in = fossil_fopen(zFilename,"rb"); |
| 287 | 298 | if( in==0 ){ |
| 288 | 299 | return 1; |
| 289 | 300 | } |
| 290 | 301 |
| --- 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 @@ | ||
| 35 | 35 | @ stashid INTEGER REFERENCES stash, -- Stash that contains this file |
| 36 | 36 | @ rid INTEGER, -- Baseline content in BLOB table or 0. |
| 37 | 37 | @ isAdded BOOLEAN, -- True if this is an added file |
| 38 | 38 | @ isRemoved BOOLEAN, -- True if this file is deleted |
| 39 | 39 | @ isExec BOOLEAN, -- True if file is executable |
| 40 | +@ isLink BOOLEAN, -- True if file is a symlink | |
| 40 | 41 | @ origname TEXT, -- Original filename |
| 41 | 42 | @ newname TEXT, -- New name for file at next check-in |
| 42 | 43 | @ delta BLOB, -- Delta from baseline. Content if rid=0 |
| 43 | 44 | @ PRIMARY KEY(origname, stashid) |
| 44 | 45 | @ ); |
| @@ -61,11 +62,11 @@ | ||
| 61 | 62 | zFile = mprintf("%/", zFName); |
| 62 | 63 | file_tree_name(zFile, &fname, 1); |
| 63 | 64 | zTreename = blob_str(&fname); |
| 64 | 65 | blob_zero(&sql); |
| 65 | 66 | blob_appendf(&sql, |
| 66 | - "SELECT deleted, isexe, mrid, pathname, coalesce(origname,pathname)" | |
| 67 | + "SELECT deleted, isexe, islink, mrid, pathname, coalesce(origname,pathname)" | |
| 67 | 68 | " FROM vfile" |
| 68 | 69 | " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)", |
| 69 | 70 | vid |
| 70 | 71 | ); |
| 71 | 72 | if( fossil_strcmp(zTreename,".")!=0 ){ |
| @@ -76,47 +77,60 @@ | ||
| 76 | 77 | ); |
| 77 | 78 | } |
| 78 | 79 | db_prepare(&q, blob_str(&sql)); |
| 79 | 80 | blob_reset(&sql); |
| 80 | 81 | db_prepare(&ins, |
| 81 | - "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec," | |
| 82 | + "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink," | |
| 82 | 83 | "origname, newname, delta)" |
| 83 | - "VALUES(%d,:rid,:isadd,:isrm,:isexe,:orig,:new,:content)", | |
| 84 | + "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)", | |
| 84 | 85 | stashid |
| 85 | 86 | ); |
| 86 | 87 | while( db_step(&q)==SQLITE_ROW ){ |
| 87 | 88 | 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); | |
| 91 | 92 | char *zPath = mprintf("%s%s", g.zLocalRoot, zName); |
| 92 | 93 | Blob content; |
| 94 | + int isNewLink = file_islink(zPath); | |
| 93 | 95 | |
| 94 | 96 | db_bind_int(&ins, ":rid", rid); |
| 95 | 97 | db_bind_int(&ins, ":isadd", rid==0); |
| 96 | 98 | db_bind_int(&ins, ":isrm", deleted); |
| 97 | 99 | db_bind_int(&ins, ":isexe", db_column_int(&q, 1)); |
| 100 | + db_bind_int(&ins, ":islink", db_column_int(&q, 2)); | |
| 98 | 101 | db_bind_text(&ins, ":orig", zOrig); |
| 99 | 102 | db_bind_text(&ins, ":new", zName); |
| 103 | + | |
| 100 | 104 | if( rid==0 ){ |
| 101 | 105 | /* 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 | + } | |
| 103 | 111 | db_bind_blob(&ins, ":content", &content); |
| 104 | 112 | }else if( deleted ){ |
| 105 | 113 | blob_zero(&content); |
| 106 | 114 | db_bind_null(&ins, ":content"); |
| 107 | 115 | }else{ |
| 108 | 116 | /* A modified file */ |
| 109 | 117 | Blob orig; |
| 110 | 118 | 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 | + } | |
| 112 | 125 | content_get(rid, &orig); |
| 113 | 126 | blob_delta_create(&orig, &disk, &content); |
| 114 | 127 | blob_reset(&orig); |
| 115 | 128 | blob_reset(&disk); |
| 116 | 129 | db_bind_blob(&ins, ":content", &content); |
| 117 | 130 | } |
| 131 | + db_bind_int(&ins, ":islink", isNewLink); | |
| 118 | 132 | db_step(&ins); |
| 119 | 133 | db_reset(&ins); |
| 120 | 134 | fossil_free(zPath); |
| 121 | 135 | blob_reset(&content); |
| 122 | 136 | } |
| @@ -167,54 +181,74 @@ | ||
| 167 | 181 | ** Apply a stash to the current check-out. |
| 168 | 182 | */ |
| 169 | 183 | static void stash_apply(int stashid, int nConflict){ |
| 170 | 184 | Stmt q; |
| 171 | 185 | db_prepare(&q, |
| 172 | - "SELECT rid, isRemoved, isExec, origname, newname, delta" | |
| 186 | + "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta" | |
| 173 | 187 | " FROM stashfile WHERE stashid=%d", |
| 174 | 188 | stashid |
| 175 | 189 | ); |
| 176 | 190 | while( db_step(&q)==SQLITE_ROW ){ |
| 177 | 191 | int rid = db_column_int(&q, 0); |
| 178 | 192 | int isRemoved = db_column_int(&q, 1); |
| 179 | 193 | 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); | |
| 182 | 197 | char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); |
| 183 | 198 | char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew); |
| 184 | 199 | Blob delta; |
| 185 | 200 | undo_save(zNew); |
| 186 | 201 | blob_zero(&delta); |
| 187 | 202 | if( rid==0 ){ |
| 188 | - db_ephemeral_blob(&q, 5, &delta); | |
| 203 | + db_ephemeral_blob(&q, 6, &delta); | |
| 189 | 204 | blob_write_to_file(&delta, zNPath); |
| 190 | 205 | file_setexe(zNPath, isExec); |
| 191 | 206 | fossil_print("ADD %s\n", zNew); |
| 192 | 207 | }else if( isRemoved ){ |
| 193 | 208 | fossil_print("DELETE %s\n", zOrig); |
| 194 | 209 | file_delete(zOPath); |
| 195 | 210 | }else{ |
| 196 | 211 | 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 | + } | |
| 199 | 219 | content_get(rid, &a); |
| 200 | 220 | 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 | + } | |
| 203 | 230 | file_setexe(zNPath, isExec); |
| 204 | 231 | fossil_print("UPDATE %s\n", zNew); |
| 205 | 232 | }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 | + } | |
| 208 | 243 | file_setexe(zNPath, isExec); |
| 209 | 244 | if( rc ){ |
| 210 | 245 | fossil_print("CONFLICT %s\n", zNew); |
| 211 | 246 | nConflict++; |
| 212 | 247 | }else{ |
| 213 | 248 | fossil_print("MERGE %s\n", zNew); |
| 214 | 249 | } |
| 215 | - blob_reset(&out); | |
| 216 | 250 | } |
| 217 | 251 | blob_reset(&a); |
| 218 | 252 | blob_reset(&b); |
| 219 | 253 | blob_reset(&disk); |
| 220 | 254 | } |
| @@ -224,11 +258,12 @@ | ||
| 224 | 258 | file_delete(zOPath); |
| 225 | 259 | } |
| 226 | 260 | } |
| 227 | 261 | db_finalize(&q); |
| 228 | 262 | 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); | |
| 230 | 265 | } |
| 231 | 266 | } |
| 232 | 267 | |
| 233 | 268 | /* |
| 234 | 269 | ** Show the diffs associate with a single stash. |
| @@ -236,41 +271,57 @@ | ||
| 236 | 271 | static void stash_diff(int stashid, const char *zDiffCmd){ |
| 237 | 272 | Stmt q; |
| 238 | 273 | Blob empty; |
| 239 | 274 | blob_zero(&empty); |
| 240 | 275 | db_prepare(&q, |
| 241 | - "SELECT rid, isRemoved, isExec, origname, newname, delta" | |
| 276 | + "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta" | |
| 242 | 277 | " FROM stashfile WHERE stashid=%d", |
| 243 | 278 | stashid |
| 244 | 279 | ); |
| 245 | 280 | while( db_step(&q)==SQLITE_ROW ){ |
| 246 | 281 | int rid = db_column_int(&q, 0); |
| 247 | 282 | 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); | |
| 250 | 286 | char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); |
| 251 | 287 | Blob delta; |
| 252 | 288 | if( rid==0 ){ |
| 253 | - db_ephemeral_blob(&q, 5, &delta); | |
| 289 | + db_ephemeral_blob(&q, 6, &delta); | |
| 254 | 290 | fossil_print("ADDED %s\n", zNew); |
| 255 | 291 | diff_print_index(zNew); |
| 256 | 292 | diff_file_mem(&empty, &delta, zNew, zDiffCmd, 0); |
| 257 | 293 | }else if( isRemoved ){ |
| 258 | 294 | 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 | + } | |
| 260 | 300 | diff_print_index(zNew); |
| 261 | 301 | diff_file_mem(&delta, &empty, zOrig, zDiffCmd, 0); |
| 262 | 302 | }else{ |
| 263 | 303 | 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 | + } | |
| 268 | 311 | 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 | + } | |
| 272 | 323 | blob_reset(&disk); |
| 273 | 324 | } |
| 274 | 325 | blob_reset(&delta); |
| 275 | 326 | } |
| 276 | 327 | db_finalize(&q); |
| @@ -352,11 +403,11 @@ | ||
| 352 | 403 | ** Forget everything about STASHID. Forget the whole stash if the |
| 353 | 404 | ** --all flag is used. Individual drops are undoable but --all is not. |
| 354 | 405 | ** |
| 355 | 406 | ** fossil stash snapshot ?-m COMMENT? ?FILES...? |
| 356 | 407 | ** |
| 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 | |
| 358 | 409 | ** but, unlike "save", do not revert those changes. |
| 359 | 410 | ** |
| 360 | 411 | ** fossil stash diff ?STASHID? |
| 361 | 412 | ** fossil stash gdiff ?STASHID? |
| 362 | 413 | ** |
| 363 | 414 |
| --- 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 @@ | ||
| 280 | 280 | const char *zName, /* Name of the object */ |
| 281 | 281 | int nName, /* Number of characters in zName */ |
| 282 | 282 | int iMode, /* Mode. 0644 or 0755 */ |
| 283 | 283 | unsigned int mTime, /* File modification time */ |
| 284 | 284 | 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 */ | |
| 286 | 287 | ){ |
| 287 | 288 | /* set mode and modification time */ |
| 288 | 289 | sqlite3_snprintf(8, (char*)&tball.aHdr[100], "%07o", iMode); |
| 289 | 290 | sqlite3_snprintf(12, (char*)&tball.aHdr[136], "%011o", mTime); |
| 290 | 291 | |
| @@ -359,20 +360,36 @@ | ||
| 359 | 360 | ** Add a single file to the growing tarball. |
| 360 | 361 | */ |
| 361 | 362 | static void tar_add_file( |
| 362 | 363 | const char *zName, /* Name of the file. nul-terminated */ |
| 363 | 364 | Blob *pContent, /* Content of the file */ |
| 364 | - int isExe, /* True for executable files */ | |
| 365 | + int mPerm, /* 1: executable file, 2: symlink */ | |
| 365 | 366 | unsigned int mTime /* Last modification time of the file */ |
| 366 | 367 | ){ |
| 367 | 368 | int nName = strlen(zName); |
| 368 | 369 | int n = blob_size(pContent); |
| 369 | 370 | int lastPage; |
| 371 | + char cType = '0'; | |
| 370 | 372 | |
| 371 | 373 | /* length check moved to tar_split_path */ |
| 372 | 374 | 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); | |
| 374 | 391 | if( n ){ |
| 375 | 392 | gzip_step(blob_buffer(pContent), n); |
| 376 | 393 | lastPage = n % 512; |
| 377 | 394 | if( lastPage!=0 ){ |
| 378 | 395 | gzip_step(tball.zSpaces, 512 - lastPage); |
| @@ -415,11 +432,11 @@ | ||
| 415 | 432 | tar_begin(); |
| 416 | 433 | for(i=3; i<g.argc; i++){ |
| 417 | 434 | blob_zero(&file); |
| 418 | 435 | blob_read_from_file(&file, g.argv[i]); |
| 419 | 436 | 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])); | |
| 421 | 438 | blob_reset(&file); |
| 422 | 439 | } |
| 423 | 440 | tar_finish(&zip); |
| 424 | 441 | blob_write_to_file(&zip, g.argv[2]); |
| 425 | 442 | } |
| 426 | 443 |
| --- 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 @@ | ||
| 30 | 30 | */ |
| 31 | 31 | static void undo_one(const char *zPathname, int redoFlag){ |
| 32 | 32 | Stmt q; |
| 33 | 33 | char *zFullname; |
| 34 | 34 | db_prepare(&q, |
| 35 | - "SELECT content, existsflag, isExe FROM undo" | |
| 35 | + "SELECT content, existsflag, isExe, isLink FROM undo" | |
| 36 | 36 | " WHERE pathname=%Q AND redoflag=%d", |
| 37 | 37 | zPathname, redoFlag |
| 38 | 38 | ); |
| 39 | 39 | if( db_step(&q)==SQLITE_ROW ){ |
| 40 | 40 | int old_exists; |
| 41 | 41 | int new_exists; |
| 42 | 42 | int old_exe; |
| 43 | 43 | int new_exe; |
| 44 | + int new_link; | |
| 45 | + int old_link; | |
| 44 | 46 | Blob current; |
| 45 | 47 | Blob new; |
| 46 | 48 | zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); |
| 49 | + old_link = db_column_int(&q, 3); | |
| 50 | + new_link = file_islink(zFullname); | |
| 47 | 51 | new_exists = file_size(zFullname)>=0; |
| 48 | 52 | if( new_exists ){ |
| 49 | - blob_read_from_file(¤t, zFullname); | |
| 53 | + if( new_link ){ | |
| 54 | + blob_read_link(¤t, zFullname); | |
| 55 | + }else{ | |
| 56 | + blob_read_from_file(¤t, zFullname); | |
| 57 | + } | |
| 50 | 58 | new_exe = file_isexe(zFullname); |
| 51 | 59 | }else{ |
| 52 | 60 | blob_zero(¤t); |
| 53 | 61 | new_exe = 0; |
| 54 | 62 | } |
| @@ -62,24 +70,31 @@ | ||
| 62 | 70 | if( new_exists ){ |
| 63 | 71 | fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname); |
| 64 | 72 | }else{ |
| 65 | 73 | fossil_print("NEW %s\n", zPathname); |
| 66 | 74 | } |
| 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 | + } | |
| 68 | 83 | file_setexe(zFullname, old_exe); |
| 69 | 84 | }else{ |
| 70 | 85 | fossil_print("DELETE %s\n", zPathname); |
| 71 | 86 | file_delete(zFullname); |
| 72 | 87 | } |
| 73 | 88 | blob_reset(&new); |
| 74 | 89 | free(zFullname); |
| 75 | 90 | db_finalize(&q); |
| 76 | 91 | 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," | |
| 78 | 93 | " redoflag=NOT redoflag" |
| 79 | 94 | " WHERE pathname=%Q", |
| 80 | - new_exists, new_exe, zPathname | |
| 95 | + new_exists, new_exe, new_link, zPathname | |
| 81 | 96 | ); |
| 82 | 97 | if( new_exists ){ |
| 83 | 98 | db_bind_blob(&q, ":c", ¤t); |
| 84 | 99 | } |
| 85 | 100 | db_step(&q); |
| @@ -209,10 +224,11 @@ | ||
| 209 | 224 | @ CREATE TABLE %s.undo( |
| 210 | 225 | @ pathname TEXT UNIQUE, -- Name of the file |
| 211 | 226 | @ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable |
| 212 | 227 | @ existsflag BOOLEAN, -- True if the file exists |
| 213 | 228 | @ isExe BOOLEAN, -- True if the file is executable |
| 229 | + @ isLink BOOLEAN, -- True if the file is symlink | |
| 214 | 230 | @ content BLOB -- Saved content |
| 215 | 231 | @ ); |
| 216 | 232 | @ CREATE TABLE %s.undo_vfile AS SELECT * FROM vfile; |
| 217 | 233 | @ CREATE TABLE %s.undo_vmerge AS SELECT * FROM vmerge; |
| 218 | 234 | ; |
| @@ -249,22 +265,27 @@ | ||
| 249 | 265 | */ |
| 250 | 266 | void undo_save(const char *zPathname){ |
| 251 | 267 | char *zFullname; |
| 252 | 268 | Blob content; |
| 253 | 269 | int existsFlag; |
| 270 | + int isLink; | |
| 254 | 271 | Stmt q; |
| 255 | 272 | |
| 256 | 273 | if( !undoActive ) return; |
| 257 | 274 | zFullname = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 258 | 275 | existsFlag = file_size(zFullname)>=0; |
| 259 | 276 | 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) | |
| 263 | 280 | ); |
| 264 | 281 | 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 | + } | |
| 266 | 287 | db_bind_blob(&q, ":c", &content); |
| 267 | 288 | } |
| 268 | 289 | free(zFullname); |
| 269 | 290 | db_step(&q); |
| 270 | 291 | db_finalize(&q); |
| 271 | 292 |
| --- 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(¤t, zFullname); |
| 50 | new_exe = file_isexe(zFullname); |
| 51 | }else{ |
| 52 | blob_zero(¤t); |
| 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", ¤t); |
| 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(¤t, zFullname); |
| 55 | }else{ |
| 56 | blob_read_from_file(¤t, zFullname); |
| 57 | } |
| 58 | new_exe = file_isexe(zFullname); |
| 59 | }else{ |
| 60 | blob_zero(¤t); |
| 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", ¤t); |
| 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 @@ | ||
| 202 | 202 | "CREATE TEMP TABLE fv(" |
| 203 | 203 | " fn TEXT PRIMARY KEY," /* The filename relative to root */ |
| 204 | 204 | " idv INTEGER," /* VFILE entry for current version */ |
| 205 | 205 | " idt INTEGER," /* VFILE entry for target version */ |
| 206 | 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 */ | |
| 207 | 209 | " ridv INTEGER," /* Record ID for current version */ |
| 208 | 210 | " ridt INTEGER," /* Record ID for target */ |
| 209 | 211 | " isexe BOOLEAN," /* Does target have execute permission? */ |
| 210 | 212 | " fnt TEXT" /* Filename of same file on target version */ |
| 211 | 213 | ");" |
| @@ -254,22 +256,35 @@ | ||
| 254 | 256 | "UPDATE fv SET" |
| 255 | 257 | " idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND pathname=fnt),0)," |
| 256 | 258 | " ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND pathname=fnt),0)", |
| 257 | 259 | tid, tid |
| 258 | 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 | + | |
| 259 | 272 | |
| 260 | 273 | if( debugFlag ){ |
| 261 | 274 | 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" | |
| 263 | 276 | ); |
| 264 | 277 | 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", | |
| 266 | 279 | db_column_int(&q, 0), |
| 267 | 280 | db_column_int(&q, 4), |
| 268 | 281 | db_column_int(&q, 5), |
| 269 | 282 | 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)); | |
| 271 | 286 | fossil_print(" fnv = [%s]\n", db_column_text(&q, 1)); |
| 272 | 287 | fossil_print(" fnt = [%s]\n", db_column_text(&q, 2)); |
| 273 | 288 | } |
| 274 | 289 | db_finalize(&q); |
| 275 | 290 | } |
| @@ -309,11 +324,11 @@ | ||
| 309 | 324 | /* |
| 310 | 325 | ** Alter the content of the checkout so that it conforms with the |
| 311 | 326 | ** target |
| 312 | 327 | */ |
| 313 | 328 | 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" | |
| 315 | 330 | ); |
| 316 | 331 | db_prepare(&mtimeXfer, |
| 317 | 332 | "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)" |
| 318 | 333 | " WHERE id=:idt" |
| 319 | 334 | ); |
| @@ -327,10 +342,12 @@ | ||
| 327 | 342 | int idt = db_column_int(&q, 3); /* VFILE entry for target */ |
| 328 | 343 | int ridt = db_column_int(&q, 4); /* RecordID for target */ |
| 329 | 344 | int chnged = db_column_int(&q, 5); /* Current is edited */ |
| 330 | 345 | const char *zNewName = db_column_text(&q,6);/* New filename */ |
| 331 | 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 */ | |
| 332 | 349 | char *zFullPath; /* Full pathname of the file */ |
| 333 | 350 | char *zFullNewPath; /* Full pathname of dest */ |
| 334 | 351 | char nameChng; /* True if the name changed */ |
| 335 | 352 | |
| 336 | 353 | zFullPath = mprintf("%s%s", g.zLocalRoot, zName); |
| @@ -381,30 +398,36 @@ | ||
| 381 | 398 | if( nameChng ){ |
| 382 | 399 | fossil_print("MERGE %s -> %s\n", zName, zNewName); |
| 383 | 400 | }else{ |
| 384 | 401 | fossil_print("MERGE %s\n", zName); |
| 385 | 402 | } |
| 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++; | |
| 399 | 407 | }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 | + } | |
| 406 | 429 | } |
| 407 | 430 | if( nameChng && !nochangeFlag ) file_delete(zFullPath); |
| 408 | 431 | blob_reset(&v); |
| 409 | 432 | blob_reset(&t); |
| 410 | 433 | blob_reset(&r); |
| @@ -522,10 +545,11 @@ | ||
| 522 | 545 | */ |
| 523 | 546 | int historical_version_of_file( |
| 524 | 547 | const char *revision, /* The checkin containing the file */ |
| 525 | 548 | const char *file, /* Full treename of the file */ |
| 526 | 549 | Blob *content, /* Put the content here */ |
| 550 | + int *pIsLink, /* Set to true if file is link. */ | |
| 527 | 551 | int *pIsExe, /* Set to true if file is executable */ |
| 528 | 552 | int errCode /* Error code if file not found. Panic if 0. */ |
| 529 | 553 | ){ |
| 530 | 554 | Manifest *pManifest; |
| 531 | 555 | ManifestFile *pFile; |
| @@ -544,11 +568,12 @@ | ||
| 544 | 568 | |
| 545 | 569 | if( pManifest ){ |
| 546 | 570 | pFile = manifest_file_seek(pManifest, file); |
| 547 | 571 | if( pFile ){ |
| 548 | 572 | 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 ); | |
| 550 | 575 | manifest_destroy(pManifest); |
| 551 | 576 | return content_get(rid, content); |
| 552 | 577 | } |
| 553 | 578 | manifest_destroy(pManifest); |
| 554 | 579 | if( errCode<=0 ){ |
| @@ -627,14 +652,15 @@ | ||
| 627 | 652 | int vid = db_lget_int("checkout", 0); |
| 628 | 653 | zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 629 | 654 | } |
| 630 | 655 | while( db_step(&q)==SQLITE_ROW ){ |
| 631 | 656 | int isExe = 0; |
| 657 | + int isLink = 0; | |
| 632 | 658 | char *zFull; |
| 633 | 659 | zFile = db_column_text(&q, 0); |
| 634 | 660 | 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); | |
| 636 | 662 | if( errCode==2 ){ |
| 637 | 663 | if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){ |
| 638 | 664 | fossil_print("UNMANAGE: %s\n", zFile); |
| 639 | 665 | }else{ |
| 640 | 666 | undo_save(zFile); |
| @@ -643,24 +669,31 @@ | ||
| 643 | 669 | } |
| 644 | 670 | db_multi_exec("DELETE FROM vfile WHERE pathname=%Q", zFile); |
| 645 | 671 | }else{ |
| 646 | 672 | sqlite3_int64 mtime; |
| 647 | 673 | 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 | + } | |
| 649 | 682 | file_setexe(zFull, isExe); |
| 650 | 683 | fossil_print("REVERTED: %s\n", zFile); |
| 651 | 684 | mtime = file_mtime(zFull); |
| 652 | 685 | db_multi_exec( |
| 653 | 686 | "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," | |
| 655 | 688 | " pathname=coalesce(origname,pathname), origname=NULL" |
| 656 | 689 | " WHERE pathname=%Q", |
| 657 | - mtime, isExe, zFile | |
| 690 | + mtime, isExe, isLink, zFile | |
| 658 | 691 | ); |
| 659 | 692 | } |
| 660 | 693 | blob_reset(&record); |
| 661 | 694 | free(zFull); |
| 662 | 695 | } |
| 663 | 696 | db_finalize(&q); |
| 664 | 697 | undo_finish(); |
| 665 | 698 | db_end_transaction(0); |
| 666 | 699 | } |
| 667 | 700 |
| --- 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 @@ | ||
| 90 | 90 | db_begin_transaction(); |
| 91 | 91 | p = manifest_get(vid, CFTYPE_MANIFEST); |
| 92 | 92 | if( p==0 ) return; |
| 93 | 93 | db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid); |
| 94 | 94 | 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)"); | |
| 97 | 97 | db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid"); |
| 98 | 98 | db_bind_int(&ins, ":vid", vid); |
| 99 | 99 | manifest_file_rewind(p); |
| 100 | 100 | while( (pFile = manifest_file_next(p,0))!=0 ){ |
| 101 | 101 | if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue; |
| @@ -110,13 +110,14 @@ | ||
| 110 | 110 | db_reset(&ridq); |
| 111 | 111 | if( rid==0 || size<0 ){ |
| 112 | 112 | fossil_warning("content missing for %s", pFile->zName); |
| 113 | 113 | continue; |
| 114 | 114 | } |
| 115 | - db_bind_int(&ins, ":isexe", manifest_file_mperm(pFile)); | |
| 115 | + db_bind_int(&ins, ":isexe", ( manifest_file_mperm(pFile)==PERM_EXE )); | |
| 116 | 116 | db_bind_int(&ins, ":id", rid); |
| 117 | 117 | db_bind_text(&ins, ":name", pFile->zName); |
| 118 | + db_bind_int(&ins, ":islink", ( manifest_file_mperm(pFile)==PERM_LNK )); | |
| 118 | 119 | db_step(&ins); |
| 119 | 120 | db_reset(&ins); |
| 120 | 121 | } |
| 121 | 122 | db_finalize(&ridq); |
| 122 | 123 | db_finalize(&ins); |
| @@ -165,11 +166,11 @@ | ||
| 165 | 166 | isDeleted = db_column_int(&q, 3); |
| 166 | 167 | oldChnged = db_column_int(&q, 4); |
| 167 | 168 | oldMtime = db_column_int64(&q, 7); |
| 168 | 169 | if( isDeleted ){ |
| 169 | 170 | chnged = 1; |
| 170 | - }else if( !file_isfile(zName) && file_size(0)>=0 ){ | |
| 171 | + }else if( !file_isfile_or_link(zName) && file_size(0)>=0 ){ | |
| 171 | 172 | if( notFileIsFatal ){ |
| 172 | 173 | fossil_warning("not an ordinary file: %s", zName); |
| 173 | 174 | nErr++; |
| 174 | 175 | } |
| 175 | 176 | chnged = 1; |
| @@ -223,29 +224,30 @@ | ||
| 223 | 224 | Stmt q; |
| 224 | 225 | Blob content; |
| 225 | 226 | int nRepos = strlen(g.zLocalRoot); |
| 226 | 227 | |
| 227 | 228 | 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" | |
| 229 | 230 | " FROM vfile" |
| 230 | 231 | " WHERE vid=%d AND mrid>0", |
| 231 | 232 | g.zLocalRoot, vid); |
| 232 | 233 | }else{ |
| 233 | 234 | 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" | |
| 235 | 236 | " FROM vfile" |
| 236 | 237 | " WHERE id=%d AND mrid>0", |
| 237 | 238 | g.zLocalRoot, id); |
| 238 | 239 | } |
| 239 | 240 | while( db_step(&q)==SQLITE_ROW ){ |
| 240 | - int id, rid, isExe; | |
| 241 | + int id, rid, isExe, isLink; | |
| 241 | 242 | const char *zName; |
| 242 | 243 | |
| 243 | 244 | id = db_column_int(&q, 0); |
| 244 | 245 | zName = db_column_text(&q, 1); |
| 245 | 246 | rid = db_column_int(&q, 2); |
| 246 | 247 | isExe = db_column_int(&q, 3); |
| 248 | + isLink = db_column_int(&q, 4); | |
| 247 | 249 | content_get(rid, &content); |
| 248 | 250 | if( file_is_the_same(&content, zName) ){ |
| 249 | 251 | blob_reset(&content); |
| 250 | 252 | if( file_setexe(zName, isExe) ){ |
| 251 | 253 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| @@ -270,11 +272,22 @@ | ||
| 270 | 272 | blob_reset(&content); |
| 271 | 273 | continue; |
| 272 | 274 | } |
| 273 | 275 | } |
| 274 | 276 | 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 | + } | |
| 276 | 289 | file_setexe(zName, isExe); |
| 277 | 290 | blob_reset(&content); |
| 278 | 291 | db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d", |
| 279 | 292 | file_mtime(zName), id); |
| 280 | 293 | } |
| @@ -380,11 +393,11 @@ | ||
| 380 | 393 | /* do nothing */ |
| 381 | 394 | }else if( file_isdir(zPath)==1 ){ |
| 382 | 395 | if( !vfile_top_of_checkout(zPath) ){ |
| 383 | 396 | vfile_scan(pPath, nPrefix, allFlag, pIgnore); |
| 384 | 397 | } |
| 385 | - }else if( file_isfile(zPath) ){ | |
| 398 | + }else if( file_isfile_or_link(zPath) ){ | |
| 386 | 399 | db_bind_text(&ins, ":file", &zPath[nPrefix+1]); |
| 387 | 400 | db_step(&ins); |
| 388 | 401 | db_reset(&ins); |
| 389 | 402 | } |
| 390 | 403 | blob_resize(pPath, origSize); |
| @@ -439,27 +452,38 @@ | ||
| 439 | 452 | const char *zName = db_column_text(&q, 1); |
| 440 | 453 | int isSelected = db_column_int(&q, 3); |
| 441 | 454 | |
| 442 | 455 | if( isSelected ){ |
| 443 | 456 | 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 | + } | |
| 461 | 485 | }else{ |
| 462 | 486 | int rid = db_column_int(&q, 4); |
| 463 | 487 | const char *zOrigName = db_column_text(&q, 2); |
| 464 | 488 | char zBuf[100]; |
| 465 | 489 | Blob file; |
| @@ -500,11 +524,15 @@ | ||
| 500 | 524 | const char *zFullpath = db_column_text(&q, 0); |
| 501 | 525 | const char *zName = db_column_text(&q, 1); |
| 502 | 526 | int rid = db_column_int(&q, 2); |
| 503 | 527 | |
| 504 | 528 | 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 | + } | |
| 506 | 534 | if( rc<0 ){ |
| 507 | 535 | fossil_print("ERROR: cannot read file [%s]\n", zFullpath); |
| 508 | 536 | blob_reset(&disk); |
| 509 | 537 | continue; |
| 510 | 538 | } |
| 511 | 539 |
| --- 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 @@ | ||
| 117 | 117 | ** Append a single file to a growing ZIP archive. |
| 118 | 118 | ** |
| 119 | 119 | ** pFile is the file to be appended. zName is the name |
| 120 | 120 | ** that the file should be saved as. |
| 121 | 121 | */ |
| 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){ | |
| 123 | 123 | z_stream stream; |
| 124 | 124 | int nameLen; |
| 125 | 125 | int toOut = 0; |
| 126 | 126 | int iStart; |
| 127 | 127 | int iCRC = 0; |
| @@ -139,11 +139,15 @@ | ||
| 139 | 139 | /* Fill in as much of the header as we know. |
| 140 | 140 | */ |
| 141 | 141 | nBlob = pFile ? blob_size(pFile) : 0; |
| 142 | 142 | if( nBlob>0 ){ |
| 143 | 143 | 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 | + } | |
| 145 | 149 | }else{ |
| 146 | 150 | iMethod = 0; |
| 147 | 151 | iMode = 040755; |
| 148 | 152 | } |
| 149 | 153 | nameLen = strlen(zName); |
| @@ -287,11 +291,11 @@ | ||
| 287 | 291 | } |
| 288 | 292 | zip_open(); |
| 289 | 293 | for(i=3; i<g.argc; i++){ |
| 290 | 294 | blob_zero(&file); |
| 291 | 295 | 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])); | |
| 293 | 297 | blob_reset(&file); |
| 294 | 298 | } |
| 295 | 299 | zip_close(&zip); |
| 296 | 300 | blob_write_to_file(&zip, g.argv[2]); |
| 297 | 301 | } |
| 298 | 302 |
| --- 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 |