| | @@ -92,66 +92,77 @@ |
| 92 | 92 | ** |
| 93 | 93 | ** Omit any file whose name is pOmit. |
| 94 | 94 | */ |
| 95 | 95 | static int add_one_file( |
| 96 | 96 | const char *zPath, /* Tree-name of file to add. */ |
| 97 | | - int vid /* Add to this VFILE */ |
| 97 | + int vid, /* Add to this VFILE */ |
| 98 | + int caseSensitive /* True if filenames are case sensitive */ |
| 98 | 99 | ){ |
| 100 | + const char *zCollate = caseSensitive ? "binary" : "nocase"; |
| 99 | 101 | if( !file_is_simple_pathname(zPath) ){ |
| 100 | 102 | fossil_fatal("filename contains illegal characters: %s", zPath); |
| 101 | 103 | } |
| 102 | | -#if defined(_WIN32) |
| 103 | 104 | if( db_exists("SELECT 1 FROM vfile" |
| 104 | | - " WHERE pathname=%Q COLLATE nocase", zPath) ){ |
| 105 | + " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){ |
| 105 | 106 | db_multi_exec("UPDATE vfile SET deleted=0" |
| 106 | | - " WHERE pathname=%Q COLLATE nocase", zPath); |
| 107 | | - } |
| 108 | | -#else |
| 109 | | - if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){ |
| 110 | | - db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath); |
| 111 | | - } |
| 112 | | -#endif |
| 113 | | - else{ |
| 107 | + " WHERE pathname=%Q COLLATE %s", zPath, zCollate); |
| 108 | + }else{ |
| 114 | 109 | char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); |
| 115 | 110 | db_multi_exec( |
| 116 | 111 | "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe)" |
| 117 | 112 | "VALUES(%d,0,0,0,%Q,%d)", |
| 118 | 113 | vid, zPath, file_isexe(zFullname)); |
| 119 | 114 | fossil_free(zFullname); |
| 120 | 115 | } |
| 121 | | - fossil_print("ADDED %s\n", zPath); |
| 122 | | - return 1; |
| 116 | + if( db_changes() ){ |
| 117 | + fossil_print("ADDED %s\n", zPath); |
| 118 | + return 1; |
| 119 | + }else{ |
| 120 | + fossil_print("SKIP %s\n", zPath); |
| 121 | + return 0; |
| 122 | + } |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | 125 | /* |
| 126 | 126 | ** Add all files in the sfile temp table. |
| 127 | 127 | ** |
| 128 | 128 | ** Automatically exclude the repository file. |
| 129 | 129 | */ |
| 130 | | -static int add_files_in_sfile(int vid){ |
| 130 | +static int add_files_in_sfile(int vid, int caseSensitive){ |
| 131 | 131 | const char *zRepo; /* Name of the repository database file */ |
| 132 | 132 | int nAdd = 0; /* Number of files added */ |
| 133 | 133 | int i; /* Loop counter */ |
| 134 | 134 | const char *zReserved; /* Name of a reserved file */ |
| 135 | 135 | Blob repoName; /* Treename of the repository */ |
| 136 | 136 | Stmt loop; /* SQL to loop over all files to add */ |
| 137 | + int (*xCmp)(const char*,const char*); |
| 137 | 138 | |
| 138 | 139 | if( !file_tree_name(g.zRepositoryName, &repoName, 0) ){ |
| 139 | 140 | blob_zero(&repoName); |
| 140 | 141 | zRepo = ""; |
| 141 | 142 | }else{ |
| 142 | 143 | zRepo = blob_str(&repoName); |
| 143 | 144 | } |
| 145 | + if( caseSensitive ){ |
| 146 | + xCmp = fossil_strcmp; |
| 147 | + }else{ |
| 148 | + xCmp = fossil_stricmp; |
| 149 | + db_multi_exec( |
| 150 | + "CREATE INDEX IF NOT EXISTS vfile_nocase" |
| 151 | + " ON vfile(pathname COLLATE nocase)" |
| 152 | + ); |
| 153 | + } |
| 154 | + xCmp = caseSensitive ? fossil_strcmp : fossil_stricmp; |
| 144 | 155 | db_prepare(&loop, "SELECT x FROM sfile ORDER BY x"); |
| 145 | 156 | while( db_step(&loop)==SQLITE_ROW ){ |
| 146 | 157 | const char *zToAdd = db_column_text(&loop, 0); |
| 147 | 158 | if( fossil_strcmp(zToAdd, zRepo)==0 ) continue; |
| 148 | 159 | for(i=0; (zReserved = fossil_reserved_name(i))!=0; i++){ |
| 149 | | - if( fossil_strcmp(zToAdd, zReserved)==0 ) break; |
| 160 | + if( xCmp(zToAdd, zReserved)==0 ) break; |
| 150 | 161 | } |
| 151 | 162 | if( zReserved ) continue; |
| 152 | | - nAdd += add_one_file(zToAdd, vid); |
| 163 | + nAdd += add_one_file(zToAdd, vid, caseSensitive); |
| 153 | 164 | } |
| 154 | 165 | db_finalize(&loop); |
| 155 | 166 | blob_reset(&repoName); |
| 156 | 167 | return nAdd; |
| 157 | 168 | } |
| | @@ -180,14 +191,17 @@ |
| 180 | 191 | int i; /* Loop counter */ |
| 181 | 192 | int vid; /* Currently checked out version */ |
| 182 | 193 | int nRoot; /* Full path characters in g.zLocalRoot */ |
| 183 | 194 | const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */ |
| 184 | 195 | Glob *pIgnore; /* Ignore everything matching this glob pattern */ |
| 196 | + int caseSensitive; /* True if filenames are case sensitive */ |
| 185 | 197 | |
| 186 | 198 | zIgnoreFlag = find_option("ignore",0,1); |
| 187 | 199 | includeDotFiles = find_option("dotfiles",0,0)!=0; |
| 200 | + capture_case_sensitive_option(); |
| 188 | 201 | db_must_be_within_tree(); |
| 202 | + caseSensitive = filenames_are_case_sensitive(); |
| 189 | 203 | if( zIgnoreFlag==0 ){ |
| 190 | 204 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 191 | 205 | } |
| 192 | 206 | vid = db_lget_int("checkout",0); |
| 193 | 207 | if( vid==0 ){ |
| | @@ -229,11 +243,11 @@ |
| 229 | 243 | } |
| 230 | 244 | blob_reset(&fullName); |
| 231 | 245 | } |
| 232 | 246 | glob_free(pIgnore); |
| 233 | 247 | |
| 234 | | - add_files_in_sfile(vid); |
| 248 | + add_files_in_sfile(vid, caseSensitive); |
| 235 | 249 | db_end_transaction(0); |
| 236 | 250 | } |
| 237 | 251 | |
| 238 | 252 | /* |
| 239 | 253 | ** COMMAND: rm |
| | @@ -289,10 +303,48 @@ |
| 289 | 303 | "UPDATE vfile SET deleted=1 WHERE pathname IN sfile;" |
| 290 | 304 | "DELETE FROM vfile WHERE rid=0 AND deleted;" |
| 291 | 305 | ); |
| 292 | 306 | db_end_transaction(0); |
| 293 | 307 | } |
| 308 | + |
| 309 | +/* |
| 310 | +** Capture the command-line --case-sensitive option. |
| 311 | +*/ |
| 312 | +static const char *zCaseSensitive = 0; |
| 313 | +void capture_case_sensitive_option(void){ |
| 314 | + if( zCaseSensitive==0 ){ |
| 315 | + zCaseSensitive = find_option("case-sensitive",0,1); |
| 316 | + } |
| 317 | +} |
| 318 | + |
| 319 | +/* |
| 320 | +** This routine determines if files should be case-sensitive or not. |
| 321 | +** In other words, this routine determines if two filenames that |
| 322 | +** differ only in case should be considered the same name or not. |
| 323 | +** |
| 324 | +** The case-sensitive setting determines the default value. If |
| 325 | +** the case-sensitive setting is undefined, then case sensitivity |
| 326 | +** defaults on for Mac and Windows and off for all other unix. |
| 327 | +** |
| 328 | +** The --case-sensitive BOOLEAN command-line option overrides any |
| 329 | +** setting. |
| 330 | +*/ |
| 331 | +int filenames_are_case_sensitive(void){ |
| 332 | + int caseSensitive; |
| 333 | + |
| 334 | + if( zCaseSensitive ){ |
| 335 | + caseSensitive = is_truth(zCaseSensitive); |
| 336 | + }else{ |
| 337 | +#if !defined(_WIN32) && !defined(__DARWIN__) && !defined(__APPLE__) |
| 338 | + caseSensitive = 1; |
| 339 | +#else |
| 340 | + caseSensitive = 0; |
| 341 | +#endif |
| 342 | + caseSensitive = db_get_boolean("case-sensitive",caseSensitive); |
| 343 | + } |
| 344 | + return caseSensitive; |
| 345 | +} |
| 294 | 346 | |
| 295 | 347 | /* |
| 296 | 348 | ** COMMAND: addremove |
| 297 | 349 | ** |
| 298 | 350 | ** Usage: %fossil addremove ?--dotfiles? ?--ignore GLOBPATTERN? ?--test? |
| | @@ -319,27 +371,29 @@ |
| 319 | 371 | ** |
| 320 | 372 | ** The --test option shows what would happen without actually doing anything. |
| 321 | 373 | ** |
| 322 | 374 | ** This command can be used to track third party software. |
| 323 | 375 | ** |
| 324 | | -** |
| 325 | 376 | ** SUMMARY: fossil addremove |
| 326 | | -** Options: ?--dotfiles? ?--ignore GLOBPATTERN? ?--test? |
| 377 | +** Options: ?--dotfiles? ?--ignore GLOB? ?--test? ?--case-sensitive BOOL? |
| 327 | 378 | */ |
| 328 | | -void import_cmd(void){ |
| 379 | +void addremove_cmd(void){ |
| 329 | 380 | Blob path; |
| 330 | 381 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 331 | 382 | int allFlag = find_option("dotfiles",0,0)!=0; |
| 332 | 383 | int isTest = find_option("test",0,0)!=0; |
| 384 | + int caseSensitive; |
| 333 | 385 | int n; |
| 334 | 386 | Stmt q; |
| 335 | 387 | int vid; |
| 336 | 388 | int nAdd = 0; |
| 337 | 389 | int nDelete = 0; |
| 338 | 390 | Glob *pIgnore; |
| 339 | 391 | |
| 392 | + capture_case_sensitive_option(); |
| 340 | 393 | db_must_be_within_tree(); |
| 394 | + caseSensitive = filenames_are_case_sensitive(); |
| 341 | 395 | if( zIgnoreFlag==0 ){ |
| 342 | 396 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 343 | 397 | } |
| 344 | 398 | vid = db_lget_int("checkout",0); |
| 345 | 399 | if( vid==0 ){ |
| | @@ -358,11 +412,11 @@ |
| 358 | 412 | blob_init(&path, g.zLocalRoot, n-1); |
| 359 | 413 | /* now we read the complete file structure into a temp table */ |
| 360 | 414 | pIgnore = glob_create(zIgnoreFlag); |
| 361 | 415 | vfile_scan(&path, blob_size(&path), allFlag, pIgnore); |
| 362 | 416 | glob_free(pIgnore); |
| 363 | | - nAdd = add_files_in_sfile(vid); |
| 417 | + nAdd = add_files_in_sfile(vid, caseSensitive); |
| 364 | 418 | |
| 365 | 419 | /* step 2: search for missing files */ |
| 366 | 420 | db_prepare(&q, |
| 367 | 421 | "SELECT pathname, %Q || pathname, deleted FROM vfile" |
| 368 | 422 | " WHERE NOT deleted" |
| 369 | 423 | |