Fossil SCM
Adjust the "fossil add" command so that on systems with case-insensitive filenames, the named added to the repository is the operating-systems preferred case for the name.
Commit
78e63995be3b2dd6a45c320c5987c68797ac6f74b5f4c84a45a14570d74675f7
Parent
5bb323ff9eb82fb…
2 files changed
+2
-1
+69
+2
-1
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -447,11 +447,11 @@ | ||
| 447 | 447 | if( isDir==1 ){ |
| 448 | 448 | vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE); |
| 449 | 449 | }else if( isDir==0 ){ |
| 450 | 450 | fossil_warning("not found: %s", zName); |
| 451 | 451 | }else{ |
| 452 | - char *zTreeName = &zName[nRoot]; | |
| 452 | + char *zTreeName = file_case_preferred_name(g.zLocalRoot,&zName[nRoot]); | |
| 453 | 453 | if( !forceFlag && glob_match(pIgnore, zTreeName) ){ |
| 454 | 454 | Blob ans; |
| 455 | 455 | char cReply; |
| 456 | 456 | char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". " |
| 457 | 457 | "Add it (a=all/y/N)? ", zTreeName); |
| @@ -468,10 +468,11 @@ | ||
| 468 | 468 | } |
| 469 | 469 | db_multi_exec( |
| 470 | 470 | "INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)", |
| 471 | 471 | zTreeName |
| 472 | 472 | ); |
| 473 | + fossil_free(zTreeName); | |
| 473 | 474 | } |
| 474 | 475 | blob_reset(&fullName); |
| 475 | 476 | } |
| 476 | 477 | glob_free(pIgnore); |
| 477 | 478 | glob_free(pClean); |
| 478 | 479 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -447,11 +447,11 @@ | |
| 447 | if( isDir==1 ){ |
| 448 | vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE); |
| 449 | }else if( isDir==0 ){ |
| 450 | fossil_warning("not found: %s", zName); |
| 451 | }else{ |
| 452 | char *zTreeName = &zName[nRoot]; |
| 453 | if( !forceFlag && glob_match(pIgnore, zTreeName) ){ |
| 454 | Blob ans; |
| 455 | char cReply; |
| 456 | char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". " |
| 457 | "Add it (a=all/y/N)? ", zTreeName); |
| @@ -468,10 +468,11 @@ | |
| 468 | } |
| 469 | db_multi_exec( |
| 470 | "INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)", |
| 471 | zTreeName |
| 472 | ); |
| 473 | } |
| 474 | blob_reset(&fullName); |
| 475 | } |
| 476 | glob_free(pIgnore); |
| 477 | glob_free(pClean); |
| 478 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -447,11 +447,11 @@ | |
| 447 | if( isDir==1 ){ |
| 448 | vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE); |
| 449 | }else if( isDir==0 ){ |
| 450 | fossil_warning("not found: %s", zName); |
| 451 | }else{ |
| 452 | char *zTreeName = file_case_preferred_name(g.zLocalRoot,&zName[nRoot]); |
| 453 | if( !forceFlag && glob_match(pIgnore, zTreeName) ){ |
| 454 | Blob ans; |
| 455 | char cReply; |
| 456 | char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". " |
| 457 | "Add it (a=all/y/N)? ", zTreeName); |
| @@ -468,10 +468,11 @@ | |
| 468 | } |
| 469 | db_multi_exec( |
| 470 | "INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)", |
| 471 | zTreeName |
| 472 | ); |
| 473 | fossil_free(zTreeName); |
| 474 | } |
| 475 | blob_reset(&fullName); |
| 476 | } |
| 477 | glob_free(pIgnore); |
| 478 | glob_free(pClean); |
| 479 |
+69
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1290,10 +1290,79 @@ | ||
| 1290 | 1290 | if( zOrigName==0 ) return 0; |
| 1291 | 1291 | blob_init(&x, 0, 0); |
| 1292 | 1292 | file_canonical_name(zOrigName, &x, 0); |
| 1293 | 1293 | return blob_str(&x); |
| 1294 | 1294 | } |
| 1295 | + | |
| 1296 | +/* | |
| 1297 | +** Convert zPath, which is a relative pathname rooted at zDir, into the | |
| 1298 | +** case preferred by the underlying filesystem. Return the a copy | |
| 1299 | +** of the converted path in memory obtained from fossil_malloc(). | |
| 1300 | +** | |
| 1301 | +** For case-sensitive filesystems, such as on Linux, this routine is | |
| 1302 | +** just fossil_strdup(). But for case-insenstiive but "case preserving" | |
| 1303 | +** filesystems, such as on MacOS or Windows, we want the filename to be | |
| 1304 | +** in the preserved casing. That's what this routine does. | |
| 1305 | +*/ | |
| 1306 | +char *file_case_preferred_name(const char *zDir, const char *zPath){ | |
| 1307 | + DIR *d; | |
| 1308 | + int i; | |
| 1309 | + char *zResult = 0; | |
| 1310 | + void *zNative = 0; | |
| 1311 | + | |
| 1312 | + if( filenames_are_case_sensitive() ){ | |
| 1313 | + return fossil_strdup(zPath); | |
| 1314 | + } | |
| 1315 | + for(i=0; zPath[i] && zPath[i]!='/' && zPath[i]!='\\'; i++){} | |
| 1316 | + zNative = fossil_utf8_to_path(zDir, 1); | |
| 1317 | + d = opendir(zNative); | |
| 1318 | + if( d ){ | |
| 1319 | + struct dirent *pEntry; | |
| 1320 | + while( (pEntry = readdir(d))!=0 ){ | |
| 1321 | + char *zUtf8 = fossil_path_to_utf8(pEntry->d_name); | |
| 1322 | + if( fossil_strnicmp(zUtf8, zPath, i)==0 && zUtf8[i]==0 ){ | |
| 1323 | + if( zPath[i]==0 ){ | |
| 1324 | + zResult = fossil_strdup(zUtf8); | |
| 1325 | + }else{ | |
| 1326 | + char *zSubDir = mprintf("%s/%s", zDir, zUtf8); | |
| 1327 | + char *zSubPath = file_case_preferred_name(zSubDir, &zPath[i+1]); | |
| 1328 | + zResult = mprintf("%s/%s", zUtf8, zSubPath); | |
| 1329 | + fossil_free(zSubPath); | |
| 1330 | + fossil_free(zSubDir); | |
| 1331 | + } | |
| 1332 | + fossil_path_free(zUtf8); | |
| 1333 | + break; | |
| 1334 | + } | |
| 1335 | + fossil_path_free(zUtf8); | |
| 1336 | + } | |
| 1337 | + closedir(d); | |
| 1338 | + } | |
| 1339 | + fossil_path_free(zNative); | |
| 1340 | + if( zResult==0 ) zResult = fossil_strdup(zPath); | |
| 1341 | + return zResult; | |
| 1342 | +} | |
| 1343 | + | |
| 1344 | +/* | |
| 1345 | +** COMMAND: test-case-filename | |
| 1346 | +** | |
| 1347 | +** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH .... | |
| 1348 | +** | |
| 1349 | +** All the PATH arguments (there must be one at least one) are pathnames | |
| 1350 | +** relative to DIRECTORY. This test command prints the OS-preferred name | |
| 1351 | +** for each PATH in filesystems where case is not significant. | |
| 1352 | +*/ | |
| 1353 | +void test_preferred_fn(void){ | |
| 1354 | + int i; | |
| 1355 | + if( g.argc<4 ){ | |
| 1356 | + usage("DIRECTORY PATH ..."); | |
| 1357 | + } | |
| 1358 | + for(i=3; i<g.argc; i++){ | |
| 1359 | + char *z = file_case_preferred_name(g.argv[2], g.argv[i]); | |
| 1360 | + fossil_print("%s -> %s\n", g.argv[i], z); | |
| 1361 | + fossil_free(z); | |
| 1362 | + } | |
| 1363 | +} | |
| 1295 | 1364 | |
| 1296 | 1365 | /* |
| 1297 | 1366 | ** The input is the name of an executable, such as one might |
| 1298 | 1367 | ** type on a command-line. This routine resolves that name into |
| 1299 | 1368 | ** a full pathname. The result is obtained from fossil_malloc() |
| 1300 | 1369 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1290,10 +1290,79 @@ | |
| 1290 | if( zOrigName==0 ) return 0; |
| 1291 | blob_init(&x, 0, 0); |
| 1292 | file_canonical_name(zOrigName, &x, 0); |
| 1293 | return blob_str(&x); |
| 1294 | } |
| 1295 | |
| 1296 | /* |
| 1297 | ** The input is the name of an executable, such as one might |
| 1298 | ** type on a command-line. This routine resolves that name into |
| 1299 | ** a full pathname. The result is obtained from fossil_malloc() |
| 1300 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1290,10 +1290,79 @@ | |
| 1290 | if( zOrigName==0 ) return 0; |
| 1291 | blob_init(&x, 0, 0); |
| 1292 | file_canonical_name(zOrigName, &x, 0); |
| 1293 | return blob_str(&x); |
| 1294 | } |
| 1295 | |
| 1296 | /* |
| 1297 | ** Convert zPath, which is a relative pathname rooted at zDir, into the |
| 1298 | ** case preferred by the underlying filesystem. Return the a copy |
| 1299 | ** of the converted path in memory obtained from fossil_malloc(). |
| 1300 | ** |
| 1301 | ** For case-sensitive filesystems, such as on Linux, this routine is |
| 1302 | ** just fossil_strdup(). But for case-insenstiive but "case preserving" |
| 1303 | ** filesystems, such as on MacOS or Windows, we want the filename to be |
| 1304 | ** in the preserved casing. That's what this routine does. |
| 1305 | */ |
| 1306 | char *file_case_preferred_name(const char *zDir, const char *zPath){ |
| 1307 | DIR *d; |
| 1308 | int i; |
| 1309 | char *zResult = 0; |
| 1310 | void *zNative = 0; |
| 1311 | |
| 1312 | if( filenames_are_case_sensitive() ){ |
| 1313 | return fossil_strdup(zPath); |
| 1314 | } |
| 1315 | for(i=0; zPath[i] && zPath[i]!='/' && zPath[i]!='\\'; i++){} |
| 1316 | zNative = fossil_utf8_to_path(zDir, 1); |
| 1317 | d = opendir(zNative); |
| 1318 | if( d ){ |
| 1319 | struct dirent *pEntry; |
| 1320 | while( (pEntry = readdir(d))!=0 ){ |
| 1321 | char *zUtf8 = fossil_path_to_utf8(pEntry->d_name); |
| 1322 | if( fossil_strnicmp(zUtf8, zPath, i)==0 && zUtf8[i]==0 ){ |
| 1323 | if( zPath[i]==0 ){ |
| 1324 | zResult = fossil_strdup(zUtf8); |
| 1325 | }else{ |
| 1326 | char *zSubDir = mprintf("%s/%s", zDir, zUtf8); |
| 1327 | char *zSubPath = file_case_preferred_name(zSubDir, &zPath[i+1]); |
| 1328 | zResult = mprintf("%s/%s", zUtf8, zSubPath); |
| 1329 | fossil_free(zSubPath); |
| 1330 | fossil_free(zSubDir); |
| 1331 | } |
| 1332 | fossil_path_free(zUtf8); |
| 1333 | break; |
| 1334 | } |
| 1335 | fossil_path_free(zUtf8); |
| 1336 | } |
| 1337 | closedir(d); |
| 1338 | } |
| 1339 | fossil_path_free(zNative); |
| 1340 | if( zResult==0 ) zResult = fossil_strdup(zPath); |
| 1341 | return zResult; |
| 1342 | } |
| 1343 | |
| 1344 | /* |
| 1345 | ** COMMAND: test-case-filename |
| 1346 | ** |
| 1347 | ** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH .... |
| 1348 | ** |
| 1349 | ** All the PATH arguments (there must be one at least one) are pathnames |
| 1350 | ** relative to DIRECTORY. This test command prints the OS-preferred name |
| 1351 | ** for each PATH in filesystems where case is not significant. |
| 1352 | */ |
| 1353 | void test_preferred_fn(void){ |
| 1354 | int i; |
| 1355 | if( g.argc<4 ){ |
| 1356 | usage("DIRECTORY PATH ..."); |
| 1357 | } |
| 1358 | for(i=3; i<g.argc; i++){ |
| 1359 | char *z = file_case_preferred_name(g.argv[2], g.argv[i]); |
| 1360 | fossil_print("%s -> %s\n", g.argv[i], z); |
| 1361 | fossil_free(z); |
| 1362 | } |
| 1363 | } |
| 1364 | |
| 1365 | /* |
| 1366 | ** The input is the name of an executable, such as one might |
| 1367 | ** type on a command-line. This routine resolves that name into |
| 1368 | ** a full pathname. The result is obtained from fossil_malloc() |
| 1369 |