Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -32,60 +32,72 @@ ** are not true files results in a fatal error. */ static void status_report( Blob *report, /* Append the status report here */ const char *zPrefix, /* Prefix on each line of the report */ - int missingIsFatal /* MISSING and NOT_A_FILE are fatal errors */ + int missingIsFatal, /* MISSING and NOT_A_FILE are fatal errors */ + int cwdRelative /* Report relative to the current working dir */ ){ Stmt q; int nPrefix = strlen(zPrefix); int nErr = 0; + Blob rewrittenPathname; db_prepare(&q, "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)" " FROM vfile " " WHERE file_is_selected(id)" " AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1" ); + blob_zero(&rewrittenPathname); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); + const char *zDisplayName = zPathname; int isDeleted = db_column_int(&q, 1); int isChnged = db_column_int(&q,2); int isNew = db_column_int(&q,3)==0; int isRenamed = db_column_int(&q,4); char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); + if( cwdRelative ){ + file_relative_name(zFullName, &rewrittenPathname); + zDisplayName = blob_str(&rewrittenPathname); + if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ + zDisplayName += 2; /* no unnecessary ./ prefix */ + } + } blob_append(report, zPrefix, nPrefix); if( isDeleted ){ - blob_appendf(report, "DELETED %s\n", zPathname); + blob_appendf(report, "DELETED %s\n", zDisplayName); }else if( !file_isfile(zFullName) ){ if( file_access(zFullName, 0)==0 ){ - blob_appendf(report, "NOT_A_FILE %s\n", zPathname); + blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName); if( missingIsFatal ){ - fossil_warning("not a file: %s", zPathname); + fossil_warning("not a file: %s", zDisplayName); nErr++; } }else{ - blob_appendf(report, "MISSING %s\n", zPathname); + blob_appendf(report, "MISSING %s\n", zDisplayName); if( missingIsFatal ){ - fossil_warning("missing file: %s", zPathname); + fossil_warning("missing file: %s", zDisplayName); nErr++; } } }else if( isNew ){ - blob_appendf(report, "ADDED %s\n", zPathname); + blob_appendf(report, "ADDED %s\n", zDisplayName); }else if( isDeleted ){ - blob_appendf(report, "DELETED %s\n", zPathname); + blob_appendf(report, "DELETED %s\n", zDisplayName); }else if( isChnged==2 ){ - blob_appendf(report, "UPDATED_BY_MERGE %s\n", zPathname); + blob_appendf(report, "UPDATED_BY_MERGE %s\n", zDisplayName); }else if( isChnged==3 ){ - blob_appendf(report, "ADDED_BY_MERGE %s\n", zPathname); + blob_appendf(report, "ADDED_BY_MERGE %s\n", zDisplayName); }else if( isChnged==1 ){ - blob_appendf(report, "EDITED %s\n", zPathname); + blob_appendf(report, "EDITED %s\n", zDisplayName); }else if( isRenamed ){ - blob_appendf(report, "RENAMED %s\n", zPathname); + blob_appendf(report, "RENAMED %s\n", zDisplayName); } free(zFullName); } + blob_reset(&rewrittenPathname); db_finalize(&q); db_prepare(&q, "SELECT uuid FROM vmerge JOIN blob ON merge=rid" " WHERE id=0"); while( db_step(&q)==SQLITE_ROW ){ blob_append(report, zPrefix, nPrefix); @@ -94,33 +106,59 @@ db_finalize(&q); if( nErr ){ fossil_fatal("aborting due to prior errors"); } } + +/* +** Use the "relative-paths" setting and the --abs-paths and +** --rel-paths command line options to determine whether the +** status report should be shown relative to the current +** working directory. +*/ +static int determine_cwd_relative_option() +{ + int relativePaths = db_get_boolean("relative-paths", 1); + int absPathOption = find_option("abs-paths", 0, 0)!=0; + int relPathOption = find_option("rel-paths", 0, 0)!=0; + if( absPathOption ){ relativePaths = 0; } + if( relPathOption ){ relativePaths = 1; } + return relativePaths; +} /* ** COMMAND: changes ** ** Usage: %fossil changes ** ** Report on the edit status of all files in the current checkout. ** See also the "status" and "extra" commands. ** +** Pathnames are displayed according to the "relative-paths" setting, +** unless overridden by the --abs-paths or --rel-paths options. +** ** Options: ** ** --sha1sum Verify file status using SHA1 hashing rather ** than relying on file mtimes. +** +** --abs-paths Display absolute pathnames. +** +** --rel-paths Display pathnames relative to the current working +** directory. */ void changes_cmd(void){ Blob report; int vid; int useSha1sum = find_option("sha1sum", 0, 0)!=0; + int cwdRelative = 0; db_must_be_within_tree(); + cwdRelative = determine_cwd_relative_option(); blob_zero(&report); vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0, useSha1sum); - status_report(&report, "", 0); + status_report(&report, "", 0, cwdRelative); blob_write_to_file(&report, "-"); } /* ** COMMAND: status @@ -127,14 +165,22 @@ ** ** Usage: %fossil status ** ** Report on the status of the current checkout. ** +** Pathnames are displayed according to the "relative-paths" setting, +** unless overridden by the --abs-paths or --rel-paths options. +** ** Options: ** ** --sha1sum Verify file status using SHA1 hashing rather ** than relying on file mtimes. +** +** --abs-paths Display absolute pathnames. +** +** --rel-paths Display pathnames relative to the current working +** directory. */ void status_cmd(void){ int vid; db_must_be_within_tree(); /* 012345678901234 */ @@ -212,22 +258,41 @@ ** ignored but can be included by adding the --dotfiles option. ** ** The GLOBPATTERN is a comma-separated list of GLOB expressions for ** files that are ignored. The GLOBPATTERN specified by the "ignore-glob" ** is used if the --ignore option is omitted. +** +** Pathnames are displayed according to the "relative-paths" setting, +** unless overridden by the --abs-paths or --rel-paths options. +** +** Options: +** +** --dotfiles Include files with names beginning with "." +** +** --ignore GLOBPATTERN +** Override the "ignore-glob" setting. +** +** --abs-paths Display absolute pathnames. +** +** --rel-paths Display pathnames relative to the current working +** directory. */ void extra_cmd(void){ Blob path; Blob repo; Stmt q; int n; const char *zIgnoreFlag = find_option("ignore",0,1); int allFlag = find_option("dotfiles",0,0)!=0; + int cwdRelative = 0; int outputManifest; Glob *pIgnore; + Blob rewrittenPathname; + const char *zPathname, *zDisplayName; db_must_be_within_tree(); + cwdRelative = determine_cwd_relative_option(); outputManifest = db_get_boolean("manifest",0); db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); n = strlen(g.zLocalRoot); blob_init(&path, g.zLocalRoot, n-1); if( zIgnoreFlag==0 ){ @@ -243,13 +308,25 @@ fossil_all_reserved_names() ); if( file_tree_name(g.zRepositoryName, &repo, 0) ){ db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); } + blob_zero(&rewrittenPathname); while( db_step(&q)==SQLITE_ROW ){ - fossil_print("%s\n", db_column_text(&q, 0)); + zDisplayName = zPathname = db_column_text(&q, 0); + if( cwdRelative ) { + char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); + file_relative_name(zFullName, &rewrittenPathname); + free(zFullName); + zDisplayName = blob_str(&rewrittenPathname); + if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ + zDisplayName += 2; /* no unnecessary ./ prefix */ + } + } + fossil_print("%s\n", zDisplayName); } + blob_reset(&rewrittenPathname); db_finalize(&q); } /* ** COMMAND: clean @@ -367,11 +444,11 @@ "# PRIVATE BRANCH: This check-in will be private and will not sync to\n" "# repositories.\n" "#\n", -1 ); } - status_report(&text, "# ", 1); + status_report(&text, "# ", 1, 0); zEditor = db_get("editor", 0); if( zEditor==0 ){ zEditor = getenv("VISUAL"); } if( zEditor==0 ){ Index: src/checkout.c ================================================================== --- src/checkout.c +++ src/checkout.c @@ -232,10 +232,11 @@ if( !keepFlag ){ vfile_to_disk(vid, 0, 1, promptFlag); } checkout_set_all_exe(vid); manifest_to_disk(vid); + ensure_empty_dirs_created(); db_lset_int("checkout", vid); undo_reset(); db_multi_exec("DELETE FROM vmerge"); if( !keepFlag && db_get_boolean("repo-cksum",1) ){ vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b); Index: src/clone.c ================================================================== --- src/clone.c +++ src/clone.c @@ -37,10 +37,11 @@ ** ** Options: ** ** --admin-user|-A USERNAME Make USERNAME the administrator ** --private Also clone private branches +** --ssl-identity=filename Use the SSL identity if requested by the server ** */ void clone_cmd(void){ char *zPassword; const char *zDefaultUser; /* Optional name of the default user */ @@ -94,10 +95,18 @@ db_initial_setup(0, zDefaultUser, 0); user_select(); db_set("content-schema", CONTENT_SCHEMA, 0); db_set("aux-schema", AUX_SCHEMA, 0); db_set("last-sync-url", g.argv[2], 0); + if( g.zSSLIdentity!=0 ){ + /* If the --ssl-identity option was specified, store it as a setting */ + Blob fn; + blob_zero(&fn); + file_canonical_name(g.zSSLIdentity, &fn); + db_set("ssl-identity", blob_str(&fn), 0); + blob_reset(&fn); + } db_multi_exec( "REPLACE INTO config(name,value,mtime)" " VALUES('server-code', lower(hex(randomblob(20))), now());" ); url_enable_proxy(0); Index: src/configure.c ================================================================== --- src/configure.c +++ src/configure.c @@ -81,10 +81,11 @@ { "project-name", CONFIGSET_PROJ }, { "project-description", CONFIGSET_PROJ }, { "manifest", CONFIGSET_PROJ }, { "ignore-glob", CONFIGSET_PROJ }, { "crnl-glob", CONFIGSET_PROJ }, + { "empty-dirs", CONFIGSET_PROJ }, { "index-page", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "ticket-table", CONFIGSET_TKT }, { "ticket-common", CONFIGSET_TKT }, Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -1399,24 +1399,93 @@ sqlite3 *dbTemp = g.db; g.db = g.dbConfig; g.dbConfig = dbTemp; } } + +/* +** Logic for reading potentially versioned settings from +** .fossil-settings/ , and emits warnings if necessary. +** Returns the non-versioned value without modification if there is no +** versioned value. +*/ +static char *db_get_do_versionable(const char *zName, char *zNonVersionedSetting){ + /* Attempt to load the versioned setting from a checked out file */ + char *zVersionedSetting = 0; + int noWarn = 0; + + if( db_open_local() ){ + Blob versionedPathname; + char *zVersionedPathname; + blob_zero(&versionedPathname); + blob_appendf(&versionedPathname, "%s/.fossil-settings/%s", + g.zLocalRoot, zName); + zVersionedPathname = blob_str(&versionedPathname); + if( file_size(zVersionedPathname)>=0 ){ + /* File exists, and contains the value for this setting. Load from + ** the file. */ + Blob setting; + blob_zero(&setting); + if( blob_read_from_file(&setting, zVersionedPathname) >= 0 ){ + blob_trim(&setting); /* Avoid non-obvious problems with line endings + ** on boolean properties */ + zVersionedSetting = strdup(blob_str(&setting)); + } + blob_reset(&setting); + /* See if there's a no-warn flag */ + blob_append(&versionedPathname, ".no-warn", -1); + if( file_size(blob_str(&versionedPathname))>=0 ){ + noWarn = 1; + } + } + blob_reset(&versionedPathname); + } + /* Display a warning? */ + if( zVersionedSetting!=0 && zNonVersionedSetting!=0 + && zNonVersionedSetting[0]!='\0' && !noWarn + ){ + /* There's a versioned setting, and a non-versioned setting. Tell + ** the user about the conflict */ + fossil_warning( + "setting %s has both versioned and non-versioned values: using " + "versioned value from file .fossil-settings/%s (to silence this " + "warning, either create an empty file named " + ".fossil-settings/%s.no-warn or delete the non-versioned setting " + " with \"fossil unset %s\")", zName, zName, zName, zName + ); + } + /* Prefer the versioned setting */ + return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; +} + /* ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the ** repository and local databases. */ char *db_get(const char *zName, char *zDefault){ char *z = 0; + int i; + const struct stControlSettings *ctrlSetting = 0; + /* Is this a setting? */ + for(i=0; ctrlSettings[i].name; i++){ + if( strcmp(ctrlSettings[i].name, zName)==0 ){ + ctrlSetting = &(ctrlSettings[i]); + break; + } + } if( g.repositoryOpen ){ z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName); } if( z==0 && g.configOpen ){ db_swap_connections(); z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName); db_swap_connections(); + } + if( ctrlSetting!=0 && ctrlSetting->versionable ){ + /* This is a versionable setting, try and get the info from a checked out file */ + z = db_get_do_versionable(zName, z); } if( z==0 ){ z = zDefault; } return z; @@ -1603,30 +1672,39 @@ } /* ** Print the value of a setting named zName */ -static void print_setting(const char *zName){ +static void print_setting(const struct stControlSettings *ctrlSetting, int localOpen){ Stmt q; if( g.repositoryOpen ){ db_prepare(&q, "SELECT '(local)', value FROM config WHERE name=%Q" " UNION ALL " "SELECT '(global)', value FROM global_config WHERE name=%Q", - zName, zName + ctrlSetting->name, ctrlSetting->name ); }else{ db_prepare(&q, "SELECT '(global)', value FROM global_config WHERE name=%Q", - zName + ctrlSetting->name ); } if( db_step(&q)==SQLITE_ROW ){ - fossil_print("%-20s %-8s %s\n", zName, db_column_text(&q, 0), + fossil_print("%-20s %-8s %s\n", ctrlSetting->name, db_column_text(&q, 0), db_column_text(&q, 1)); }else{ - fossil_print("%-20s\n", zName); + fossil_print("%-20s\n", ctrlSetting->name); + } + if( ctrlSetting->versionable && localOpen ){ + /* Check to see if this is overridden by a versionable settings file */ + Blob versionedPathname; + blob_zero(&versionedPathname); + blob_appendf(&versionedPathname, "%s/.fossil-settings/%s", g.zLocalRoot, ctrlSetting->name); + if( file_size(blob_str(&versionedPathname))>=0 ){ + fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", ctrlSetting->name); + } } db_finalize(&q); } @@ -1642,44 +1720,49 @@ #if INTERFACE struct stControlSettings { char const *name; /* Name of the setting */ char const *var; /* Internal variable name used by db_set() */ int width; /* Width of display. 0 for boolean values */ + int versionable; /* Is this setting versionable? */ char const *def; /* Default value */ }; #endif /* INTERFACE */ struct stControlSettings const ctrlSettings[] = { - { "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" }, - { "editor", 0, 16, "" }, - { "gdiff-command", 0, 16, "gdiff" }, - { "gmerge-command",0, 40, "" }, - { "https-login", 0, 0, "off" }, - { "ignore-glob", 0, 40, "" }, - { "http-port", 0, 16, "8080" }, - { "localauth", 0, 0, "off" }, - { "main-branch", 0, 40, "trunk" }, - { "manifest", 0, 0, "off" }, - { "max-upload", 0, 25, "250000" }, - { "mtime-changes", 0, 0, "on" }, - { "pgp-command", 0, 32, "gpg --clearsign -o " }, - { "proxy", 0, 32, "off" }, - { "repo-cksum", 0, 0, "on" }, - { "self-register", 0, 0, "off" }, - { "ssh-command", 0, 32, "" }, - { "web-browser", 0, 32, "" }, - { "white-foreground", 0, 0, "off" }, - { 0,0,0,0 } + { "access-log", 0, 0, 0, "off" }, + { "auto-captcha", "autocaptcha", 0, 0, "on" }, + { "auto-shun", 0, 0, 0, "on" }, + { "autosync", 0, 0, 0, "on" }, + { "binary-glob", 0, 32, 1, "" }, + { "clearsign", 0, 0, 0, "off" }, + { "case-sensitive",0, 0, 0, "on" }, + { "crnl-glob", 0, 16, 1, "" }, + { "default-perms", 0, 16, 0, "u" }, + { "diff-command", 0, 16, 0, "" }, + { "dont-push", 0, 0, 0, "off" }, + { "editor", 0, 16, 0, "" }, + { "gdiff-command", 0, 16, 0, "gdiff" }, + { "gmerge-command",0, 40, 0, "" }, + { "https-login", 0, 0, 0, "off" }, + { "ignore-glob", 0, 40, 1, "" }, + { "empty-dirs", 0, 40, 1, "" }, + { "http-port", 0, 16, 0, "8080" }, + { "localauth", 0, 0, 0, "off" }, + { "main-branch", 0, 40, 0, "trunk" }, + { "manifest", 0, 0, 1, "off" }, + { "max-upload", 0, 25, 0, "250000" }, + { "mtime-changes", 0, 0, 0, "on" }, + { "pgp-command", 0, 32, 0, "gpg --clearsign -o " }, + { "proxy", 0, 32, 0, "off" }, + { "relative-paths",0, 0, 0, "on" }, + { "repo-cksum", 0, 0, 0, "on" }, + { "self-register", 0, 0, 0, "off" }, + { "ssl-ca-location",0, 40, 0, "" }, + { "ssl-identity", 0, 40, 0, "" }, + { "ssh-command", 0, 32, 0, "" }, + { "web-browser", 0, 32, 0, "" }, + { "white-foreground", 0, 0, 0, "off" }, + { 0,0,0,0,0 } }; /* ** COMMAND: settings ** COMMAND: unset @@ -1688,10 +1771,14 @@ ** %fossil unset PROPERTY ?-global? ** ** The "settings" command with no arguments lists all properties and their ** values. With just a property name it shows the value of that property. ** With a value argument it changes the property for the current repository. +** +** Settings marked as versionable are overridden by the contents of the +** file named .fossil-settings/PROPERTY in the checked out files, if that +** file exists. ** ** The "unset" command clears a property setting. ** ** ** auto-captcha If enabled, the Login page provides a button to @@ -1705,13 +1792,13 @@ ** or update and automatically push after commit or ** tag or branch creation. If the value is "pullonly" ** then only pull operations occur automatically. ** 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 +** binary-glob The VALUE is a comma or newline-separated list of +** (versionable) 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. @@ -1718,12 +1805,12 @@ ** ** clearsign When enabled, fossil will attempt to sign all commits ** with gpg. When disabled (the default), commits will ** be unsigned. Default: off ** -** crnl-glob A comma-separated list of GLOB patterns for text files -** in which it is ok to have CR+NL line endings. +** crnl-glob A comma or newline-separated list of GLOB patterns for +** (versionable) text files in which it is ok to have CR+NL line endings. ** Set to "*" to disable CR+NL checking. ** ** default-perms Permissions given automatically to new users. For more ** information on permissions see Users page in Server ** Administration of the HTTP UI. Default: u. @@ -1731,10 +1818,15 @@ ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. +** +** empty-dirs A comma or newline-separated list of pathnames. On +** (versionable) update and checkout commands, if no file or directory +** exists with that name, an empty directory will be +** created. ** ** editor Text editor command used for check-in comments. ** ** gdiff-command External command to run when performing a graphical ** diff. If undefined, text diff will be used. @@ -1749,23 +1841,23 @@ ** and "ui" commands. Default: 8080 ** ** https-login Send login creditials using HTTPS instead of HTTP ** even if the login page request came via HTTP. ** -** ignore-glob The VALUE is a comma-separated list of GLOB patterns -** specifying files that the "extra" command will ignore. -** Example: *.o,*.obj,*.exe +** ignore-glob The VALUE is a comma or newline-separated list of GLOB +** (versionable) patterns specifying files that the "extra" command will +** ignore. Example: *.o,*.obj,*.exe ** ** localauth If enabled, require that HTTP connections from ** 127.0.0.1 be authenticated by password. If ** false, all HTTP requests from localhost have ** unrestricted access to the repository. ** ** main-branch The primary branch for the project. Default: trunk ** ** manifest If enabled, automatically create files "manifest" and -** "manifest.uuid" in every checkout. The SQLite and +** (versionable) "manifest.uuid" in every checkout. The SQLite and ** Fossil repositories both require this. Default: off. ** ** max-upload A limit on the size of uplink HTTP requests. The ** default is 250000 bytes. ** @@ -1777,10 +1869,13 @@ ** ** proxy URL of the HTTP proxy. If undefined or "off" then ** the "http_proxy" environment variable is consulted. ** If the http_proxy environment variable is undefined ** then a direct HTTP connection is used. +** +** relative-paths When showing changes and extras, report paths relative +** to the current working directory. Default: "on" ** ** repo-cksum Compute checksums over all files in each checkout ** as a double-check of correctness. Defaults to "on". ** Disable on large repositories for a performance ** improvement. @@ -1787,10 +1882,28 @@ ** ** self-register Allow users to register themselves through the HTTP UI. ** This is useful if you want to see other names than ** "Anonymous" in e.g. ticketing system. On the other hand ** users can not be deleted. Default: off. +** +** ssl-ca-location The full pathname to a file containing PEM encoded +** CA root certificates, or a directory of certificates +** with filenames formed from the certificate hashes as +** required by OpenSSL. +** If set, this will override the OS default list of +** OpenSSL CAs. If unset, the default list will be used. +** Some platforms may add additional certificates. +** Check your platform behaviour is as required if the +** exact contents of the CA root is critical for your +** application. +** +** ssl-identity The full pathname to a file containing a certificate +** and private key in PEM format. Create by concatenating +** the certificate and private key files. +** This identity will be presented to SSL servers to +** authenticate this client, in addition to the normal +** password authentication. ** ** ssh-command Command used to talk to a remote machine with ** the "ssh://" protocol. ** ** web-browser A shell command used to launch your preferred @@ -1811,12 +1924,13 @@ } if( unsetFlag && g.argc!=3 ){ usage("PROPERTY ?-global?"); } if( g.argc==2 ){ + int openLocal = db_open_local(); for(i=0; ctrlSettings[i].name; i++){ - print_setting(ctrlSettings[i].name); + print_setting(&ctrlSettings[i], openLocal); } }else if( g.argc==3 || g.argc==4 ){ const char *zName = g.argv[2]; int isManifest; int n = strlen(zName); @@ -1834,11 +1948,11 @@ db_unset(ctrlSettings[i].name, globalFlag); }else if( g.argc==4 ){ db_set(ctrlSettings[i].name, g.argv[3], globalFlag); }else{ isManifest = 0; - print_setting(ctrlSettings[i].name); + print_setting(&ctrlSettings[i], db_open_local()); } if( isManifest && g.localOpen ){ manifest_to_disk(db_lget_int("checkout", 0)); } }else{ Index: src/file.c ================================================================== --- src/file.c +++ src/file.c @@ -499,26 +499,45 @@ } } } return 1; } + +/* +** Return a pointer to the first character in a pathname past the +** drive letter. This routine is a no-op on unix. +*/ +char *file_without_drive_letter(char *zIn){ +#ifdef _WIN32 + if( fossil_isalpha(zIn[0]) && zIn[1]==':' ) zIn += 2; +#endif + return zIn; +} /* ** Compute a pathname for a file or directory that is relative ** to the current directory. */ void file_relative_name(const char *zOrigName, Blob *pOut){ char *zPath; blob_set(pOut, zOrigName); blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); - zPath = blob_buffer(pOut); + zPath = file_without_drive_letter(blob_buffer(pOut)); if( zPath[0]=='/' ){ int i, j; Blob tmp; - char zPwd[2000]; - file_getcwd(zPwd, sizeof(zPwd)-20); - for(i=1; zPath[i] && zPwd[i]==zPath[i]; i++){} + char *zPwd; + char zBuf[2000]; + zPwd = zBuf; + file_getcwd(zBuf, sizeof(zBuf)-20); + zPwd = file_without_drive_letter(zBuf); + i = 1; +#ifdef _WIN32 + while( zPath[i] && fossil_tolower(zPwd[i])==fossil_tolower(zPath[i]) ) i++; +#else + while( zPath[i] && zPwd[i]==zPath[i] ) i++; +#endif if( zPath[i]==0 ){ blob_reset(pOut); if( zPwd[i]==0 ){ blob_append(pOut, ".", 1); }else{ Index: src/glob.c ================================================================== --- src/glob.c +++ src/glob.c @@ -110,24 +110,24 @@ p = fossil_malloc( sizeof(*p) + nList+1 ); memset(p, 0, sizeof(*p)); z = (char*)&p[1]; memcpy(z, zPatternList, nList+1); while( z[0] ){ - while( z[0]==',' || z[0]==' ' ) z++; /* Skip leading spaces */ + while( z[0]==',' || z[0]==' ' || z[0]=='\n' || z[0]=='\r' ) z++; /* Skip leading spaces and newlines */ if( z[0]=='\'' || z[0]=='"' ){ delimiter = z[0]; z++; }else{ delimiter = ','; } if( z[0]==0 ) break; p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) ); p->azPattern[p->nPattern++] = z; - for(i=0; z[i] && z[i]!=delimiter; i++){} + for(i=0; z[i] && z[i]!=delimiter && z[i]!='\n' && z[i]!='\r'; i++){} if( delimiter==',' ){ - /* Remove trailing spaces on a comma-delimited pattern */ - for(j=i; j>1 && z[j-1]==' '; j--){} + /* Remove trailing spaces / newlines on a comma-delimited pattern */ + for(j=i; j>1 && (z[j-1]==' ' || z[j-1]=='\n' || z[j-1]=='\r'); j--){} if( jname!=0; pSet++){ if( pSet->width==0 ){ onoff_attribute(pSet->name, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, is_truth(pSet->def)); - @
+ if( pSet->versionable ){ + @ (v)
+ } else { + @
+ } } } @ for(pSet=ctrlSettings; pSet->name!=0; pSet++){ if( pSet->width!=0 ){ entry_attribute(pSet->name, /*pSet->width*/ 40, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, (char*)pSet->def); - @
+ if( pSet->versionable ){ + @ (v)
+ } else { + @
+ } } } @ @

@ + @

Settings marked with (v) are 'versionable' and will be overridden by the contents of files named .fossil-settings/PROPERTY.

@

@ These settings work in the same way, as the set commandline:
@

%s(zHelp_setting_cmd)
db_end_transaction(0); style_footer(); Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -115,10 +115,13 @@ } if( !nochangeFlag && db_exists("SELECT 1 FROM vmerge") ){ fossil_fatal("cannot update an uncommitted merge"); } if( !nochangeFlag && !internalUpdate ) autosync(AUTOSYNC_PULL); + + /* Create any empty directories now, as well as after the update, so changes in settings are reflected now */ + ensure_empty_dirs_created(); if( internalUpdate ){ tid = internalUpdate; }else if( g.argc>=3 ){ if( fossil_strcmp(g.argv[2], "current")==0 ){ @@ -441,10 +444,11 @@ ** Clean up the mid and pid VFILE entries. Then commit the changes. */ if( nochangeFlag ){ db_end_transaction(1); /* With --nochange, rollback changes */ }else{ + ensure_empty_dirs_created(); if( g.argc<=3 ){ /* All files updated. Shift the current checkout to the target. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid); checkout_set_all_exe(tid); manifest_to_disk(tid); @@ -456,10 +460,63 @@ } if( !internalUpdate ) undo_finish(); db_end_transaction(0); } } + +/* +** Make sure empty directories are created +*/ +void ensure_empty_dirs_created(void){ + /* Make empty directories? */ + char *zEmptyDirs = db_get("empty-dirs", 0); + if( zEmptyDirs!=0 ){ + char *bc; + Blob dirName; + Blob dirsList; + + blob_zero(&dirsList); + blob_init(&dirsList, zEmptyDirs, strlen(zEmptyDirs)); + /* Replace commas by spaces */ + bc = blob_str(&dirsList); + while( (*bc)!='\0' ){ + if( (*bc)==',' ) { *bc = ' '; } + ++bc; + } + /* Make directories */ + blob_zero(&dirName); + while( blob_token(&dirsList, &dirName) ){ + const char *zDir = blob_str(&dirName); + /* Make full pathname of the directory */ + Blob path; + const char *zPath; + + blob_zero(&path); + blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir); + zPath = blob_str(&path); + /* Handle various cases of existence of the directory */ + switch( file_isdir(zPath) ){ + case 0: { /* doesn't exist */ + if( file_mkdir(zPath, 0)!=0 ) { + fossil_warning("couldn't create directory %s as " + "required by empty-dirs setting", zDir); + } + break; + } + case 1: { /* exists, and is a directory */ + /* do nothing - required directory exists already */ + break; + } + case 2: { /* exists, but isn't a directory */ + fossil_warning("file %s found, but a directory is required " + "by empty-dirs setting", zDir); + } + } + blob_reset(&path); + } + } +} /* ** Get the contents of a file within the checking "revision". If ** revision==NULL then get the file content for the current checkout. Index: www/changes.wiki ================================================================== --- www/changes.wiki +++ www/changes.wiki @@ -3,10 +3,25 @@

Changes For Version 1.19 (pending)

* Added a ./configure script based on autosetup. * Added the "[/help/winsrv | fossil winsrv]" command for creating a Fossil service on windows systems. + * Added "versionable settings" where settings that affect + the local tree can be stored in versioned files in the + .fossil-settings directory. + * The status, changes and extras commands now show + pathnames relative to the current working directory, + unless overridden by command line options or the + "relative-paths" setting.
WARNING: This + change will break scripts which rely on the current + output when the current working directory is not the + repository root. + * Added "empty-dirs" versionable setting. + * Added support for client-side SSL certificates with "ssl-identity" + setting and --ssl-identity option. + * Added "ssl-ca-location" setting to specify trusted root + SSL certificates.

Changes For Version 1.18 (2011-07-14)

* Added this Change Log * Added sequential version numbering Index: www/index.wiki ================================================================== --- www/index.wiki +++ www/index.wiki @@ -127,10 +127,12 @@ * The [./selfcheck.wiki | automatic self-check] mechanism helps insure project integrity. * Fossil contains a [./wikitheory.wiki | built-in wiki]. * An [./event.wiki | Event] is a special kind of wiki page associated with a point in time rather than a name. + * [./settings.wiki | Settings] control the behaviour of fossil. + * [./ssl.wiki | Use SSL] to encrypt communication with the server. * There is a [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list] (with publicly readable [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives] available for discussing fossil issues. * [./stats.wiki | Performance statistics] taken from real-world projects Index: www/mkindex.tcl ================================================================== --- www/mkindex.tcl +++ www/mkindex.tcl @@ -9,10 +9,11 @@ set doclist { bugtheory.wiki {Bug Tracking In Fossil} branching.wiki {Branching, Forking, Merging, and Tagging} build.wiki {Building and Installing Fossil} checkin_names.wiki {Checkin And Version Names} + changes.wiki {Fossil Changelog} copyright-release.html {Contributor License Agreement} concepts.wiki {Fossil Core Concepts} contribute.wiki {Contributing Code or Documentation To The Fossil Project} custom_ticket.wiki {Customizing The Ticket System} delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm} @@ -35,13 +36,15 @@ {Quotes: What People Are Saying About Fossil, Git, and DVCSes in General} ../test/release-checklist.wiki {Pre-Release Testing Checklist} selfcheck.wiki {Fossil Repository Integrity Self Checks} selfhost.wiki {Fossil Self Hosting Repositories} server.wiki {How To Configure A Fossil Server} + settings.wiki {Fossil Settings} shunning.wiki {Shunning: Deleting Content From Fossil} stats.wiki {Performance Statistics} style.wiki {Source Code Style Guidelines} + ssl.wiki {Using SSL with Fossil} sync.wiki {The Fossil Sync Protocol} tech_overview.wiki {A Technical Overview Of The Design And Implementation Of Fossil} tech_overview.wiki {SQLite Databases Used By Fossil} theory1.wiki {Thoughts On The Design Of The Fossil DVCS} Index: www/permutedindex.wiki ================================================================== --- www/permutedindex.wiki +++ www/permutedindex.wiki @@ -11,10 +11,11 @@
  • Branches — Creating, Syncing, and Deleting Private
  • Branching, Forking, Merging, and Tagging
  • Bug Tracking In Fossil
  • Build Process — The Fossil
  • Building and Installing Fossil
  • +
  • Changelog — Fossil
  • Checkin And Version Names
  • Checklist — Pre-Release Testing
  • Checklist For Successful Open-Source Projects
  • Checks — Fossil Repository Integrity Self
  • Code or Documentation To The Fossil Project — Contributing
  • @@ -44,17 +45,19 @@
  • Export To And From Git — Import And
  • File Format — Fossil
  • Forking, Merging, and Tagging — Branching,
  • Format — Fossil Delta
  • Format — Fossil File
  • +
  • Fossil Changelog
  • Fossil Core Concepts
  • Fossil Delta Encoding Algorithm
  • Fossil Delta Format
  • Fossil File Format
  • Fossil Quick Start Guide
  • Fossil Repository Integrity Self Checks
  • Fossil Self Hosting Repositories
  • +
  • Fossil Settings
  • Fossil Versus Git
  • Fossil, Git, and DVCSes in General — Quotes: What People Are Saying About
  • Frequently Asked Questions
  • From Fossil — Shunning: Deleting Content
  • From Git — Import And Export To And
  • @@ -99,13 +102,15 @@
  • Repository Integrity Self Checks — Fossil
  • Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are
  • Self Checks — Fossil Repository Integrity
  • Self Hosting Repositories — Fossil
  • Server — How To Configure A Fossil
  • +
  • Settings — Fossil
  • Shunning: Deleting Content From Fossil
  • Source Code Style Guidelines
  • SQLite Databases Used By Fossil
  • +
  • SSL with Fossil — Using
  • Start Guide — Fossil Quick
  • Statistics — Performance
  • Style Guidelines — Source Code
  • Successful Open-Source Projects — Checklist For
  • Sync Protocol — The Fossil
  • @@ -118,11 +123,13 @@
  • The Fossil Sync Protocol
  • The Fossil Web Interface
  • Thoughts On The Design Of The Fossil DVCS
  • Ticket System — Customizing The
  • Tracking In Fossil — Bug
  • +
  • Using SSL with Fossil
  • Version Names — Checkin And
  • Versus Git — Fossil
  • Web Interface — The Fossil
  • What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:
  • Wiki In Fossil
  • +
  • with Fossil — Using SSL
  • Index: www/server.wiki ================================================================== --- www/server.wiki +++ www/server.wiki @@ -97,10 +97,13 @@ If you are using "inetd" to serve your repository, then you simply need to add "/usr/bin/stunnel" (perhaps on a different path, depending on your setup) before the command line to launch Fossil.

    At this stage, the standalone server (e.g. "fossil server") does not support SSL.

    +

    +For more information, see Using SSL with Fossil. +

    Various security concerns with hosted repositories

    There are two main concerns relating to usage of Fossil for sharing sensitive information (source or any other data): ADDED www/settings.wiki Index: www/settings.wiki ================================================================== --- /dev/null +++ www/settings.wiki @@ -0,0 +1,31 @@ +Fossil Settings + +

    Using Fossil Settings

    + +Settings control the behaviour of fossil. They are set with the fossil settings command, or through the web interface in the Settings page in the Admin section. + +For a list of all settings, view the Settings page, or type fossil help settings from the command line. + + +

    Repository settings

    + +Settings are set on a per-repository basis. When you clone a repository, a subset of settings are copied to your local repository. + +If you make a change to a setting on your local repository, it is not synced back to the server when you push or sync. If you make a change on the server, you need to manually make the change on all repositories which are cloned from this repository. + +You can also set a setting globally on your local machine. The value will be used for all repositories cloned to your machine, unless overridden explicitly in a particular repository. Global settings can be set by using the -global option on the fossil settings command. + + +

    "Versionable" settings

    + +Most of the settings control the behaviour of fossil on your local machine, largely acting to reflect your preference on how you want to use Fossil, how you communicate with the server, or options for hosting a repository on the web. + +However, for historical reasons, some settings affect how you work with versioned files. These are binary-glob, crnl-glob, ignore-glob, empty-dirs and manifest. The most important is ignore-glob which specifies which files should be ignored when looking for unmanaged files with the extras command. + +Because these options can change over time, and the inconvenience of replicating changes, these settings are "versionable". As well as being able to be set using the settings command or the web interface, you can created versioned files in the .fossil-settings directory named with the setting name. The contents of the file is the value of the setting, and these files are checked in, committed, merged, and so on, as with any other file. + +Where a setting is a list of values, such as ignore-glob, you can also a newline as a separator as well as a comma. + +For example, to set the list of ignored files, create a .fossil-settings/ignore-glob file where each line contains a glob for ignored files. + +If you set the value of a setting using the settings command as well as a versioned file, the versioned setting will take precedence. A warning will be displayed. ADDED www/ssl.wiki Index: www/ssl.wiki ================================================================== --- /dev/null +++ www/ssl.wiki @@ -0,0 +1,36 @@ +SSL and Fossil + +

    Using SSL with Fossil

    + +If you are storing sensitive information in your repository, you should use SSL to encrypt all communications. This will protect the credentials used to access the server, as well preventing eavesdropping of the contents of your repository. + +To host a repository with SSL, you need to use an web server which supports SSL in front of the Fossil server. You can host it using the CGI option or by proxying Fossil's built in HTTP server. + +Your fossil client must be built with SSL support. The configure script will attempt to find OpenSSL on your system, but if necessary, you can specify the location with the --with-openssl option. Type ./configure --help for details. + +Make sure the URL you clone from uses the https: scheme to ensure you're using SSL. If your server is configured to serve the repository from http as well as https, it's easy to accidentally use unencrypted HTTP if you forget the all important 's'. + + +

    Certificates

    + +To verify the identify of a server, SSL uses certificates. Fossil needs to know which certificates you trust. + +If you are using a self-signed certificate, you'll be asked if you want to accept the certificate the first time you communicate with the server. Verify the certificate fingerprint is correct, then answer "always" to remember your decision. + +If you are using a certificate signed by a certificate authority, you need to specify the certificates you trust with the ssl-ca-location setting. Set this globally with the -global option for convenience. + +This should be set to the location of a file containing all the PEM encoded certificates you trust. You can obtain a certificate using a web browser, for example, Firefox, or just refer to your system's trusted CA roots which are usually stored somewhere in /etc. + + +

    Client side certificates

    + +You can also use client side certificates to add an extra layer of authentication, over and above Fossil's built in user management. If you are particularly paranoid, you'll want to use this to remove the ability of anyone on the internet from making any request to Fossil. Without presenting a valid client side certificate, the web server won't invoke the fossil CGI handler. + +Configure your server to request a client side certificate, and set up a certificate authority to sign your client certificates. For each person who needs to access the repository, create a private key and certificate signed with that CA. + +The PEM encoded private key and certificate should be stored in a single file, simply by concatenating the key and certificate files. Specify the location of this file with the ssl-identity setting, or the --ssl-identity option to the clone command. + +If you've password protected the private key, the password will be requested every time you connect to the server. This password is not stored by fossil, as doing so would defeat the purpose of having a password. + +If you attempt to connect to a server which requests a client certificate, but don't provide one, fossil will show an error message which explains what to do to authenticate with the server. +