Fossil SCM
More improvements to "fossil open": Make sure the --repodir is converted into a full pathname so that it is unaffected by --workdir. Report an error with --nested if the new repo would be rooted in the same directory as another repository.
Commit
15a7b1fd377dd67b4c302ad708fd918ad380e9e383599c699233b724155028d4
Parent
9f3747d8a5542a0…
2 files changed
+15
-7
+17
M
src/db.c
+15
-7
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1714,16 +1714,20 @@ | ||
| 1714 | 1714 | ** |
| 1715 | 1715 | ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and |
| 1716 | 1716 | ** try again. Once the file is found, the g.zLocalRoot variable is set |
| 1717 | 1717 | ** to the root of the repository tree and this routine returns 1. If |
| 1718 | 1718 | ** no database is found, then this routine return 0. |
| 1719 | +** | |
| 1720 | +** In db_open_local_v2(), if the bRootOnly flag is true, then only | |
| 1721 | +** look in the CWD for the checkout database. Do not scan upwards in | |
| 1722 | +** the file hierarchy. | |
| 1719 | 1723 | ** |
| 1720 | 1724 | ** This routine always opens the user database regardless of whether or |
| 1721 | 1725 | ** not the repository database is found. If the _FOSSIL_ or .fslckout file |
| 1722 | 1726 | ** is found, it is attached to the open database connection too. |
| 1723 | 1727 | */ |
| 1724 | -int db_open_local(const char *zDbName){ | |
| 1728 | +int db_open_local_v2(const char *zDbName, int bRootOnly){ | |
| 1725 | 1729 | int i, n; |
| 1726 | 1730 | char zPwd[2000]; |
| 1727 | 1731 | static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; |
| 1728 | 1732 | |
| 1729 | 1733 | if( g.localOpen ) return 1; |
| @@ -1747,18 +1751,22 @@ | ||
| 1747 | 1751 | g.localOpen = 1; |
| 1748 | 1752 | db_open_repository(zDbName); |
| 1749 | 1753 | return 1; |
| 1750 | 1754 | } |
| 1751 | 1755 | } |
| 1756 | + if( bRootOnly ) break; | |
| 1752 | 1757 | n--; |
| 1753 | 1758 | while( n>1 && zPwd[n]!='/' ){ n--; } |
| 1754 | 1759 | while( n>1 && zPwd[n-1]=='/' ){ n--; } |
| 1755 | 1760 | zPwd[n] = 0; |
| 1756 | 1761 | } |
| 1757 | 1762 | |
| 1758 | 1763 | /* A checkout database file could not be found */ |
| 1759 | 1764 | return 0; |
| 1765 | +} | |
| 1766 | +int db_open_local(const char *zDbName){ | |
| 1767 | + return db_open_local_v2(zDbName, 0); | |
| 1760 | 1768 | } |
| 1761 | 1769 | |
| 1762 | 1770 | /* |
| 1763 | 1771 | ** Get the full pathname to the repository database file. The |
| 1764 | 1772 | ** local database (the _FOSSIL_ or .fslckout database) must have already |
| @@ -3121,11 +3129,10 @@ | ||
| 3121 | 3129 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3122 | 3130 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3123 | 3131 | const char *zWorkDir; /* --workdir value */ |
| 3124 | 3132 | const char *zRepo = 0; /* Name of the repository file */ |
| 3125 | 3133 | const char *zRepoDir = 0; /* --repodir value */ |
| 3126 | - Blob normalizedRepoName; /* Normalized repository filename */ | |
| 3127 | 3134 | char *zPwd; /* Initial working directory */ |
| 3128 | 3135 | int isUri = 0; /* True if REPOSITORY is a URI */ |
| 3129 | 3136 | |
| 3130 | 3137 | url_proxy_options(); |
| 3131 | 3138 | emptyFlag = find_option("empty",0,0)!=0; |
| @@ -3143,11 +3150,10 @@ | ||
| 3143 | 3150 | |
| 3144 | 3151 | if( g.argc!=3 && g.argc!=4 ){ |
| 3145 | 3152 | usage("REPOSITORY-FILENAME ?VERSION?"); |
| 3146 | 3153 | } |
| 3147 | 3154 | zRepo = g.argv[2]; |
| 3148 | - blob_init(&normalizedRepoName, 0, 0); | |
| 3149 | 3155 | if( sqlite3_strglob("http://*", zRepo)==0 |
| 3150 | 3156 | || sqlite3_strglob("https://*", zRepo)==0 |
| 3151 | 3157 | || sqlite3_strglob("ssh:*", zRepo)==0 |
| 3152 | 3158 | || sqlite3_strglob("file:*", zRepo)==0 |
| 3153 | 3159 | ){ |
| @@ -3155,12 +3161,14 @@ | ||
| 3155 | 3161 | } |
| 3156 | 3162 | |
| 3157 | 3163 | /* If --workdir is specified, change to the requested working directory */ |
| 3158 | 3164 | if( zWorkDir ){ |
| 3159 | 3165 | if( !isUri ){ |
| 3160 | - file_canonical_name(zRepo, &normalizedRepoName, 0); | |
| 3161 | - zRepo = blob_str(&normalizedRepoName); | |
| 3166 | + zRepo = file_canonical_name_dup(zRepo); | |
| 3167 | + } | |
| 3168 | + if( zRepoDir ){ | |
| 3169 | + zRepoDir = file_canonical_name_dup(zRepoDir); | |
| 3162 | 3170 | } |
| 3163 | 3171 | if( file_isdir(zWorkDir, ExtFILE)!=1 ){ |
| 3164 | 3172 | file_mkfolder(zWorkDir, ExtFILE, 0, 0); |
| 3165 | 3173 | if( file_mkdir(zWorkDir, ExtFILE, 0) ){ |
| 3166 | 3174 | fossil_fatal("cannot create directory %s", zWorkDir); |
| @@ -3169,12 +3177,12 @@ | ||
| 3169 | 3177 | if( file_chdir(zWorkDir, 0) ){ |
| 3170 | 3178 | fossil_fatal("unable to make %s the working directory", zWorkDir); |
| 3171 | 3179 | } |
| 3172 | 3180 | } |
| 3173 | 3181 | |
| 3174 | - if( !allowNested && db_open_local(0) ){ | |
| 3175 | - fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot); | |
| 3182 | + if( db_open_local_v2(0, allowNested) ){ | |
| 3183 | + fossil_fatal("there is already an open tree at %s", g.zLocalRoot); | |
| 3176 | 3184 | } |
| 3177 | 3185 | |
| 3178 | 3186 | /* If REPOSITORY looks like a URI, then try to clone it first */ |
| 3179 | 3187 | if( isUri ){ |
| 3180 | 3188 | char *zNewBase; /* Base name of the cloned repository file */ |
| 3181 | 3189 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1714,16 +1714,20 @@ | |
| 1714 | ** |
| 1715 | ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and |
| 1716 | ** try again. Once the file is found, the g.zLocalRoot variable is set |
| 1717 | ** to the root of the repository tree and this routine returns 1. If |
| 1718 | ** no database is found, then this routine return 0. |
| 1719 | ** |
| 1720 | ** This routine always opens the user database regardless of whether or |
| 1721 | ** not the repository database is found. If the _FOSSIL_ or .fslckout file |
| 1722 | ** is found, it is attached to the open database connection too. |
| 1723 | */ |
| 1724 | int db_open_local(const char *zDbName){ |
| 1725 | int i, n; |
| 1726 | char zPwd[2000]; |
| 1727 | static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; |
| 1728 | |
| 1729 | if( g.localOpen ) return 1; |
| @@ -1747,18 +1751,22 @@ | |
| 1747 | g.localOpen = 1; |
| 1748 | db_open_repository(zDbName); |
| 1749 | return 1; |
| 1750 | } |
| 1751 | } |
| 1752 | n--; |
| 1753 | while( n>1 && zPwd[n]!='/' ){ n--; } |
| 1754 | while( n>1 && zPwd[n-1]=='/' ){ n--; } |
| 1755 | zPwd[n] = 0; |
| 1756 | } |
| 1757 | |
| 1758 | /* A checkout database file could not be found */ |
| 1759 | return 0; |
| 1760 | } |
| 1761 | |
| 1762 | /* |
| 1763 | ** Get the full pathname to the repository database file. The |
| 1764 | ** local database (the _FOSSIL_ or .fslckout database) must have already |
| @@ -3121,11 +3129,10 @@ | |
| 3121 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3122 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3123 | const char *zWorkDir; /* --workdir value */ |
| 3124 | const char *zRepo = 0; /* Name of the repository file */ |
| 3125 | const char *zRepoDir = 0; /* --repodir value */ |
| 3126 | Blob normalizedRepoName; /* Normalized repository filename */ |
| 3127 | char *zPwd; /* Initial working directory */ |
| 3128 | int isUri = 0; /* True if REPOSITORY is a URI */ |
| 3129 | |
| 3130 | url_proxy_options(); |
| 3131 | emptyFlag = find_option("empty",0,0)!=0; |
| @@ -3143,11 +3150,10 @@ | |
| 3143 | |
| 3144 | if( g.argc!=3 && g.argc!=4 ){ |
| 3145 | usage("REPOSITORY-FILENAME ?VERSION?"); |
| 3146 | } |
| 3147 | zRepo = g.argv[2]; |
| 3148 | blob_init(&normalizedRepoName, 0, 0); |
| 3149 | if( sqlite3_strglob("http://*", zRepo)==0 |
| 3150 | || sqlite3_strglob("https://*", zRepo)==0 |
| 3151 | || sqlite3_strglob("ssh:*", zRepo)==0 |
| 3152 | || sqlite3_strglob("file:*", zRepo)==0 |
| 3153 | ){ |
| @@ -3155,12 +3161,14 @@ | |
| 3155 | } |
| 3156 | |
| 3157 | /* If --workdir is specified, change to the requested working directory */ |
| 3158 | if( zWorkDir ){ |
| 3159 | if( !isUri ){ |
| 3160 | file_canonical_name(zRepo, &normalizedRepoName, 0); |
| 3161 | zRepo = blob_str(&normalizedRepoName); |
| 3162 | } |
| 3163 | if( file_isdir(zWorkDir, ExtFILE)!=1 ){ |
| 3164 | file_mkfolder(zWorkDir, ExtFILE, 0, 0); |
| 3165 | if( file_mkdir(zWorkDir, ExtFILE, 0) ){ |
| 3166 | fossil_fatal("cannot create directory %s", zWorkDir); |
| @@ -3169,12 +3177,12 @@ | |
| 3169 | if( file_chdir(zWorkDir, 0) ){ |
| 3170 | fossil_fatal("unable to make %s the working directory", zWorkDir); |
| 3171 | } |
| 3172 | } |
| 3173 | |
| 3174 | if( !allowNested && db_open_local(0) ){ |
| 3175 | fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot); |
| 3176 | } |
| 3177 | |
| 3178 | /* If REPOSITORY looks like a URI, then try to clone it first */ |
| 3179 | if( isUri ){ |
| 3180 | char *zNewBase; /* Base name of the cloned repository file */ |
| 3181 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1714,16 +1714,20 @@ | |
| 1714 | ** |
| 1715 | ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and |
| 1716 | ** try again. Once the file is found, the g.zLocalRoot variable is set |
| 1717 | ** to the root of the repository tree and this routine returns 1. If |
| 1718 | ** no database is found, then this routine return 0. |
| 1719 | ** |
| 1720 | ** In db_open_local_v2(), if the bRootOnly flag is true, then only |
| 1721 | ** look in the CWD for the checkout database. Do not scan upwards in |
| 1722 | ** the file hierarchy. |
| 1723 | ** |
| 1724 | ** This routine always opens the user database regardless of whether or |
| 1725 | ** not the repository database is found. If the _FOSSIL_ or .fslckout file |
| 1726 | ** is found, it is attached to the open database connection too. |
| 1727 | */ |
| 1728 | int db_open_local_v2(const char *zDbName, int bRootOnly){ |
| 1729 | int i, n; |
| 1730 | char zPwd[2000]; |
| 1731 | static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; |
| 1732 | |
| 1733 | if( g.localOpen ) return 1; |
| @@ -1747,18 +1751,22 @@ | |
| 1751 | g.localOpen = 1; |
| 1752 | db_open_repository(zDbName); |
| 1753 | return 1; |
| 1754 | } |
| 1755 | } |
| 1756 | if( bRootOnly ) break; |
| 1757 | n--; |
| 1758 | while( n>1 && zPwd[n]!='/' ){ n--; } |
| 1759 | while( n>1 && zPwd[n-1]=='/' ){ n--; } |
| 1760 | zPwd[n] = 0; |
| 1761 | } |
| 1762 | |
| 1763 | /* A checkout database file could not be found */ |
| 1764 | return 0; |
| 1765 | } |
| 1766 | int db_open_local(const char *zDbName){ |
| 1767 | return db_open_local_v2(zDbName, 0); |
| 1768 | } |
| 1769 | |
| 1770 | /* |
| 1771 | ** Get the full pathname to the repository database file. The |
| 1772 | ** local database (the _FOSSIL_ or .fslckout database) must have already |
| @@ -3121,11 +3129,10 @@ | |
| 3129 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 3130 | static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; |
| 3131 | const char *zWorkDir; /* --workdir value */ |
| 3132 | const char *zRepo = 0; /* Name of the repository file */ |
| 3133 | const char *zRepoDir = 0; /* --repodir value */ |
| 3134 | char *zPwd; /* Initial working directory */ |
| 3135 | int isUri = 0; /* True if REPOSITORY is a URI */ |
| 3136 | |
| 3137 | url_proxy_options(); |
| 3138 | emptyFlag = find_option("empty",0,0)!=0; |
| @@ -3143,11 +3150,10 @@ | |
| 3150 | |
| 3151 | if( g.argc!=3 && g.argc!=4 ){ |
| 3152 | usage("REPOSITORY-FILENAME ?VERSION?"); |
| 3153 | } |
| 3154 | zRepo = g.argv[2]; |
| 3155 | if( sqlite3_strglob("http://*", zRepo)==0 |
| 3156 | || sqlite3_strglob("https://*", zRepo)==0 |
| 3157 | || sqlite3_strglob("ssh:*", zRepo)==0 |
| 3158 | || sqlite3_strglob("file:*", zRepo)==0 |
| 3159 | ){ |
| @@ -3155,12 +3161,14 @@ | |
| 3161 | } |
| 3162 | |
| 3163 | /* If --workdir is specified, change to the requested working directory */ |
| 3164 | if( zWorkDir ){ |
| 3165 | if( !isUri ){ |
| 3166 | zRepo = file_canonical_name_dup(zRepo); |
| 3167 | } |
| 3168 | if( zRepoDir ){ |
| 3169 | zRepoDir = file_canonical_name_dup(zRepoDir); |
| 3170 | } |
| 3171 | if( file_isdir(zWorkDir, ExtFILE)!=1 ){ |
| 3172 | file_mkfolder(zWorkDir, ExtFILE, 0, 0); |
| 3173 | if( file_mkdir(zWorkDir, ExtFILE, 0) ){ |
| 3174 | fossil_fatal("cannot create directory %s", zWorkDir); |
| @@ -3169,12 +3177,12 @@ | |
| 3177 | if( file_chdir(zWorkDir, 0) ){ |
| 3178 | fossil_fatal("unable to make %s the working directory", zWorkDir); |
| 3179 | } |
| 3180 | } |
| 3181 | |
| 3182 | if( db_open_local_v2(0, allowNested) ){ |
| 3183 | fossil_fatal("there is already an open tree at %s", g.zLocalRoot); |
| 3184 | } |
| 3185 | |
| 3186 | /* If REPOSITORY looks like a URI, then try to clone it first */ |
| 3187 | if( isUri ){ |
| 3188 | char *zNewBase; /* Base name of the cloned repository file */ |
| 3189 |
+17
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1105,10 +1105,12 @@ | ||
| 1105 | 1105 | ** Remove redundant / characters |
| 1106 | 1106 | ** Remove all /./ path elements. |
| 1107 | 1107 | ** Convert /A/../ to just / |
| 1108 | 1108 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1109 | 1109 | ** is retained. |
| 1110 | +** | |
| 1111 | +** See also: file_canonical_name_dup() | |
| 1110 | 1112 | */ |
| 1111 | 1113 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1112 | 1114 | blob_zero(pOut); |
| 1113 | 1115 | if( file_is_absolute_path(zOrigName) ){ |
| 1114 | 1116 | blob_appendf(pOut, "%/", zOrigName); |
| @@ -1140,10 +1142,25 @@ | ||
| 1140 | 1142 | } |
| 1141 | 1143 | #endif |
| 1142 | 1144 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1143 | 1145 | blob_size(pOut), slash)); |
| 1144 | 1146 | } |
| 1147 | + | |
| 1148 | +/* | |
| 1149 | +** Compute the canonical name of a file. Store that name in | |
| 1150 | +** memory obtained from fossil_malloc() and return a pointer to the | |
| 1151 | +** name. | |
| 1152 | +** | |
| 1153 | +** See also: file_canonical_name() | |
| 1154 | +*/ | |
| 1155 | +char *file_canonical_name_dup(const char *zOrigName){ | |
| 1156 | + Blob x; | |
| 1157 | + if( zOrigName==0 ) return 0; | |
| 1158 | + blob_init(&x, 0, 0); | |
| 1159 | + file_canonical_name(zOrigName, &x, 0); | |
| 1160 | + return blob_str(&x); | |
| 1161 | +} | |
| 1145 | 1162 | |
| 1146 | 1163 | /* |
| 1147 | 1164 | ** The input is the name of an executable, such as one might |
| 1148 | 1165 | ** type on a command-line. This routine resolves that name into |
| 1149 | 1166 | ** a full pathname. The result is obtained from fossil_malloc() |
| 1150 | 1167 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1105,10 +1105,12 @@ | |
| 1105 | ** Remove redundant / characters |
| 1106 | ** Remove all /./ path elements. |
| 1107 | ** Convert /A/../ to just / |
| 1108 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1109 | ** is retained. |
| 1110 | */ |
| 1111 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1112 | blob_zero(pOut); |
| 1113 | if( file_is_absolute_path(zOrigName) ){ |
| 1114 | blob_appendf(pOut, "%/", zOrigName); |
| @@ -1140,10 +1142,25 @@ | |
| 1140 | } |
| 1141 | #endif |
| 1142 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1143 | blob_size(pOut), slash)); |
| 1144 | } |
| 1145 | |
| 1146 | /* |
| 1147 | ** The input is the name of an executable, such as one might |
| 1148 | ** type on a command-line. This routine resolves that name into |
| 1149 | ** a full pathname. The result is obtained from fossil_malloc() |
| 1150 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1105,10 +1105,12 @@ | |
| 1105 | ** Remove redundant / characters |
| 1106 | ** Remove all /./ path elements. |
| 1107 | ** Convert /A/../ to just / |
| 1108 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 1109 | ** is retained. |
| 1110 | ** |
| 1111 | ** See also: file_canonical_name_dup() |
| 1112 | */ |
| 1113 | void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ |
| 1114 | blob_zero(pOut); |
| 1115 | if( file_is_absolute_path(zOrigName) ){ |
| 1116 | blob_appendf(pOut, "%/", zOrigName); |
| @@ -1140,10 +1142,25 @@ | |
| 1142 | } |
| 1143 | #endif |
| 1144 | blob_resize(pOut, file_simplify_name(blob_buffer(pOut), |
| 1145 | blob_size(pOut), slash)); |
| 1146 | } |
| 1147 | |
| 1148 | /* |
| 1149 | ** Compute the canonical name of a file. Store that name in |
| 1150 | ** memory obtained from fossil_malloc() and return a pointer to the |
| 1151 | ** name. |
| 1152 | ** |
| 1153 | ** See also: file_canonical_name() |
| 1154 | */ |
| 1155 | char *file_canonical_name_dup(const char *zOrigName){ |
| 1156 | Blob x; |
| 1157 | if( zOrigName==0 ) return 0; |
| 1158 | blob_init(&x, 0, 0); |
| 1159 | file_canonical_name(zOrigName, &x, 0); |
| 1160 | return blob_str(&x); |
| 1161 | } |
| 1162 | |
| 1163 | /* |
| 1164 | ** The input is the name of an executable, such as one might |
| 1165 | ** type on a command-line. This routine resolves that name into |
| 1166 | ** a full pathname. The result is obtained from fossil_malloc() |
| 1167 |