Index: src/add.c ================================================================== --- src/add.c +++ src/add.c @@ -92,66 +92,77 @@ ** ** Omit any file whose name is pOmit. */ static int add_one_file( const char *zPath, /* Tree-name of file to add. */ - int vid /* Add to this VFILE */ + int vid, /* Add to this VFILE */ + int caseSensitive /* True if filenames are case sensitive */ ){ + const char *zCollate = caseSensitive ? "binary" : "nocase"; if( !file_is_simple_pathname(zPath) ){ fossil_fatal("filename contains illegal characters: %s", zPath); } -#if defined(_WIN32) if( db_exists("SELECT 1 FROM vfile" - " WHERE pathname=%Q COLLATE nocase", zPath) ){ + " WHERE pathname=%Q COLLATE %s", zPath, zCollate) ){ db_multi_exec("UPDATE vfile SET deleted=0" - " WHERE pathname=%Q COLLATE nocase", zPath); - } -#else - if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){ - db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath); - } -#endif - else{ + " WHERE pathname=%Q COLLATE %s", zPath, zCollate); + }else{ char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); db_multi_exec( "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe)" "VALUES(%d,0,0,0,%Q,%d)", vid, zPath, file_isexe(zFullname)); fossil_free(zFullname); } - fossil_print("ADDED %s\n", zPath); - return 1; + if( db_changes() ){ + fossil_print("ADDED %s\n", zPath); + return 1; + }else{ + fossil_print("SKIP %s\n", zPath); + return 0; + } } /* ** Add all files in the sfile temp table. ** ** Automatically exclude the repository file. */ -static int add_files_in_sfile(int vid){ +static int add_files_in_sfile(int vid, int caseSensitive){ const char *zRepo; /* Name of the repository database file */ int nAdd = 0; /* Number of files added */ int i; /* Loop counter */ const char *zReserved; /* Name of a reserved file */ Blob repoName; /* Treename of the repository */ Stmt loop; /* SQL to loop over all files to add */ + int (*xCmp)(const char*,const char*); if( !file_tree_name(g.zRepositoryName, &repoName, 0) ){ blob_zero(&repoName); zRepo = ""; }else{ zRepo = blob_str(&repoName); } + if( caseSensitive ){ + xCmp = fossil_strcmp; + }else{ + xCmp = fossil_stricmp; + db_multi_exec( + "CREATE INDEX IF NOT EXISTS vfile_nocase" + " ON vfile(pathname COLLATE nocase)" + ); + } + xCmp = caseSensitive ? fossil_strcmp : fossil_stricmp; db_prepare(&loop, "SELECT x FROM sfile ORDER BY x"); while( db_step(&loop)==SQLITE_ROW ){ const char *zToAdd = db_column_text(&loop, 0); if( fossil_strcmp(zToAdd, zRepo)==0 ) continue; for(i=0; (zReserved = fossil_reserved_name(i))!=0; i++){ - if( fossil_strcmp(zToAdd, zReserved)==0 ) break; + if( xCmp(zToAdd, zReserved)==0 ) break; } if( zReserved ) continue; - nAdd += add_one_file(zToAdd, vid); + nAdd += add_one_file(zToAdd, vid, caseSensitive); } db_finalize(&loop); blob_reset(&repoName); return nAdd; } @@ -180,14 +191,17 @@ int i; /* Loop counter */ int vid; /* Currently checked out version */ int nRoot; /* Full path characters in g.zLocalRoot */ const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */ Glob *pIgnore; /* Ignore everything matching this glob pattern */ + int caseSensitive; /* True if filenames are case sensitive */ zIgnoreFlag = find_option("ignore",0,1); includeDotFiles = find_option("dotfiles",0,0)!=0; + capture_case_sensitive_option(); db_must_be_within_tree(); + caseSensitive = filenames_are_case_sensitive(); if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } vid = db_lget_int("checkout",0); if( vid==0 ){ @@ -229,11 +243,11 @@ } blob_reset(&fullName); } glob_free(pIgnore); - add_files_in_sfile(vid); + add_files_in_sfile(vid, caseSensitive); db_end_transaction(0); } /* ** COMMAND: rm @@ -289,10 +303,48 @@ "UPDATE vfile SET deleted=1 WHERE pathname IN sfile;" "DELETE FROM vfile WHERE rid=0 AND deleted;" ); db_end_transaction(0); } + +/* +** Capture the command-line --case-sensitive option. +*/ +static const char *zCaseSensitive = 0; +void capture_case_sensitive_option(void){ + if( zCaseSensitive==0 ){ + zCaseSensitive = find_option("case-sensitive",0,1); + } +} + +/* +** This routine determines if files should be case-sensitive or not. +** In other words, this routine determines if two filenames that +** differ only in case should be considered the same name or not. +** +** The case-sensitive setting determines the default value. If +** the case-sensitive setting is undefined, then case sensitivity +** defaults on for Mac and Windows and off for all other unix. +** +** The --case-sensitive BOOLEAN command-line option overrides any +** setting. +*/ +int filenames_are_case_sensitive(void){ + int caseSensitive; + + if( zCaseSensitive ){ + caseSensitive = is_truth(zCaseSensitive); + }else{ +#if !defined(_WIN32) && !defined(__DARWIN__) && !defined(__APPLE__) + caseSensitive = 1; +#else + caseSensitive = 0; +#endif + caseSensitive = db_get_boolean("case-sensitive",caseSensitive); + } + return caseSensitive; +} /* ** COMMAND: addremove ** ** Usage: %fossil addremove ?--dotfiles? ?--ignore GLOBPATTERN? ?--test? @@ -319,27 +371,29 @@ ** ** The --test option shows what would happen without actually doing anything. ** ** This command can be used to track third party software. ** -** ** SUMMARY: fossil addremove -** Options: ?--dotfiles? ?--ignore GLOBPATTERN? ?--test? +** Options: ?--dotfiles? ?--ignore GLOB? ?--test? ?--case-sensitive BOOL? */ -void import_cmd(void){ +void addremove_cmd(void){ Blob path; const char *zIgnoreFlag = find_option("ignore",0,1); int allFlag = find_option("dotfiles",0,0)!=0; int isTest = find_option("test",0,0)!=0; + int caseSensitive; int n; Stmt q; int vid; int nAdd = 0; int nDelete = 0; Glob *pIgnore; + capture_case_sensitive_option(); db_must_be_within_tree(); + caseSensitive = filenames_are_case_sensitive(); if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } vid = db_lget_int("checkout",0); if( vid==0 ){ @@ -358,11 +412,11 @@ blob_init(&path, g.zLocalRoot, n-1); /* now we read the complete file structure into a temp table */ pIgnore = glob_create(zIgnoreFlag); vfile_scan(&path, blob_size(&path), allFlag, pIgnore); glob_free(pIgnore); - nAdd = add_files_in_sfile(vid); + nAdd = add_files_in_sfile(vid, caseSensitive); /* step 2: search for missing files */ db_prepare(&q, "SELECT pathname, %Q || pathname, deleted FROM vfile" " WHERE NOT deleted" Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -1366,19 +1366,19 @@ */ int is_truth(const char *zVal){ static const char *azOn[] = { "on", "yes", "true", "1" }; int i; for(i=0; i<sizeof(azOn)/sizeof(azOn[0]); i++){ - if( fossil_strcmp(zVal,azOn[i])==0 ) return 1; + if( fossil_stricmp(zVal,azOn[i])==0 ) return 1; } return 0; } int is_false(const char *zVal){ static const char *azOff[] = { "off", "no", "false", "0" }; int i; for(i=0; i<sizeof(azOff)/sizeof(azOff[0]); i++){ - if( fossil_strcmp(zVal,azOff[i])==0 ) return 1; + if( fossil_stricmp(zVal,azOff[i])==0 ) return 1; } return 0; } /* @@ -1647,10 +1647,11 @@ { "access-log", 0, 0, "off" }, { "auto-captcha", "autocaptcha", 0, "on" }, { "auto-shun", 0, 0, "on" }, { "autosync", 0, 0, "on" }, { "binary-glob", 0, 32, "" }, + { "case-sensitive",0, 0, "on" }, { "clearsign", 0, 0, "off" }, { "crnl-glob", 0, 16, "" }, { "default-perms", 0, 16, "u" }, { "diff-command", 0, 16, "" }, { "dont-push", 0, 0, "off" }, @@ -1703,10 +1704,15 @@ ** Default: on ** ** binary-glob The VALUE is a comma-separated list of GLOB patterns ** that should be treated as binary files for merging ** purposes. Example: *.xml +** +** case-sensitive If TRUE, the files whose names differ only in case +** care considered distinct. If FALSE files whose names +** differ only in case are the same file. Defaults to +** TRUE for unix and FALSE for windows and mac. ** ** clearsign When enabled, fossil will attempt to sign all commits ** with gpg. When disabled (the default), commits will ** be unsigned. Default: off ** Index: src/merge.c ================================================================== --- src/merge.c +++ src/merge.c @@ -53,10 +53,14 @@ ** and do not try to merge parallel changes. This ** option overrides the "binary-glob" setting. ** ** --nochange | -n Dryrun: do not actually make any changes; just ** show what would have happened. +** +** --case-sensitive BOOL Overwrite the case-sensitive setting. If false, +** files whose names differ only in case are taken +** to be the same file. */ void merge_cmd(void){ int vid; /* Current version "V" */ int mid; /* Version we are merging from "M" */ int pid; /* The pivot version - most recent common ancestor P */ @@ -69,10 +73,11 @@ int debugFlag; /* True if --debug is present */ int nChng; /* Number of file name changes */ int *aChng; /* An array of file name changes */ int i; /* Loop counter */ int nConflict = 0; /* Number of conflicts seen */ + int caseSensitive; /* True for case-sensitive filenames */ Stmt q; /* Notation: ** @@ -87,14 +92,16 @@ backoutFlag = find_option("backout",0,0)!=0; debugFlag = find_option("debug",0,0)!=0; zBinGlob = find_option("binary",0,1); nochangeFlag = find_option("nochange","n",0)!=0; zPivot = find_option("baseline",0,1); + capture_case_sensitive_option(); if( g.argc!=3 ){ usage("VERSION"); } db_must_be_within_tree(); + caseSensitive = filenames_are_case_sensitive(); if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("nothing is checked out"); } @@ -150,11 +157,11 @@ ** in the current checkout, the pivot, and the version being merged. */ db_multi_exec( "DROP TABLE IF EXISTS fv;" "CREATE TEMP TABLE fv(" - " fn TEXT PRIMARY KEY," /* The filename */ + " fn TEXT PRIMARY KEY COLLATE %s," /* The filename */ " idv INTEGER," /* VFILE entry for current version */ " idp INTEGER," /* VFILE entry for the pivot */ " idm INTEGER," /* VFILE entry for version merging in */ " chnged BOOLEAN," /* True if current version has been edited */ " ridv INTEGER," /* Record ID for current version */ @@ -161,11 +168,12 @@ " ridp INTEGER," /* Record ID for pivot */ " ridm INTEGER," /* Record ID for merge */ " isexe BOOLEAN," /* Execute permission enabled */ " fnp TEXT," /* The filename in the pivot */ " fnm TEXT" /* the filename in the merged version */ - ");" + ");", + caseSensitive ? "binary" : "nocase" ); /* Add files found in V */ db_multi_exec(