Fossil SCM

Allow the REPOSITORY argument to "fossil open" to be a URI, in which case the URI is cloned first and then the clone is opened.

drh 2020-08-07 13:59 trunk
Commit dfc5ceed730b6a659f20ca009bf405f53ac1f0498089a52d00922bcef0946840
1 file changed +66 -11
+66 -11
--- src/db.c
+++ src/db.c
@@ -3070,29 +3070,41 @@
30703070
}
30713071
30723072
/*
30733073
** COMMAND: open
30743074
**
3075
-** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS?
3075
+** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS?
30763076
**
3077
-** Open a connection to the local repository in FILENAME. A checkout
3078
-** for the repository is created with its root at the working directory.
3077
+** Open a new connection to the repository name REPOSITORY. A checkout
3078
+** for the repository is created with its root at the current working
3079
+** directory, or at some other directory identified by "--workdir DIR".
30793080
** If VERSION is specified then that version is checked out. Otherwise
3080
-** the latest version is checked out. No files other than "manifest"
3081
-** and "manifest.uuid" are modified if the --keep option is present.
3081
+** the most recent check-in on the main branch (usually "trunk") is used.
3082
+**
3083
+** REPOSITORY is usually a filename for a repository that already exists
3084
+** on the local machine. But REPOSITORY can also be a URI, in which case
3085
+** the URI is first cloned and the clone is opened. The clone will be stored
3086
+** in the current directory, or in an alternative directory specified by
3087
+** the --repodir option.
3088
+**
3089
+** No files other than "manifest" and "manifest.uuid" are modified if
3090
+** the --keep option is present.
30823091
**
30833092
** Options:
30843093
** --empty Initialize checkout as being empty, but still connected
30853094
** with the local repository. If you commit this checkout,
30863095
** it will become a new "initial" commit in the repository.
3096
+** --force-missing Force opening a repository with missing content
30873097
** --keep Only modify the manifest and manifest.uuid files
30883098
** --nested Allow opening a repository inside an opened checkout
3089
-** --force-missing Force opening a repository with missing content
3099
+** --repodir DIR If REPOSITORY is a URI that will be cloned, store
3100
+** the clone in DIR rather than in "."
30903101
** --setmtime Set timestamps of all files to match their SCM-side
30913102
** times (the timestamp of the last checkin which modified
30923103
** them).
3093
-** --workdir DIR Use DIR as the working directory instead of ".".
3104
+** --workdir DIR Use DIR as the working directory instead of ".". The DIR
3105
+** directory is created if it does not previously exist.
30943106
**
30953107
** See also: close
30963108
*/
30973109
void cmd_open(void){
30983110
int emptyFlag;
@@ -3102,19 +3114,21 @@
31023114
int allowSymlinks;
31033115
int setmtimeFlag; /* --setmtime. Set mtimes on files */
31043116
static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
31053117
const char *zWorkDir; /* --workdir value */
31063118
const char *zRepo = 0; /* Name of the repository file */
3119
+ const char *zRepoDir = 0; /* --repodir value */
31073120
Blob normalizedRepoName; /* Normalized repository filename */
31083121
31093122
url_proxy_options();
31103123
emptyFlag = find_option("empty",0,0)!=0;
31113124
keepFlag = find_option("keep",0,0)!=0;
31123125
forceMissingFlag = find_option("force-missing",0,0)!=0;
31133126
allowNested = find_option("nested",0,0)!=0;
31143127
setmtimeFlag = find_option("setmtime",0,0)!=0;
31153128
zWorkDir = find_option("workdir",0,1);
3129
+ zRepoDir = find_option("repodir",0,1);
31163130
31173131
31183132
/* We should be done with options.. */
31193133
verify_all_options();
31203134
@@ -3121,23 +3135,64 @@
31213135
if( g.argc!=3 && g.argc!=4 ){
31223136
usage("REPOSITORY-FILENAME ?VERSION?");
31233137
}
31243138
zRepo = g.argv[2];
31253139
blob_init(&normalizedRepoName, 0, 0);
3140
+
3141
+ /* If REPOSITORY looks like a URI, then try to clone it first */
3142
+ if( sqlite3_strglob("http://*", zRepo)==0
3143
+ || sqlite3_strglob("https://*", zRepo)==0
3144
+ || sqlite3_strglob("ssh:*", zRepo)==0
3145
+ || sqlite3_strglob("file:*", zRepo)==0
3146
+ ){
3147
+ char *zNewBase; /* Base name of the cloned repository file */
3148
+ const char *zUri; /* URI to clone */
3149
+ int i; /* Loop counter */
3150
+ int rc; /* Result code from fossil_system() */
3151
+ Blob cmd; /* Clone command to be run */
3152
+ char *zCmd; /* String version of the clone command */
3153
+
3154
+ zUri = zRepo;
3155
+ zNewBase = fossil_strdup(file_tail(zUri));
3156
+ for(i=(int)strlen(zNewBase)-1; i>1 && zNewBase[i]!='.'; i--){}
3157
+ if( zNewBase[i]=='.' ) zNewBase[i] = 0;
3158
+ if( zRepoDir==0 ) zRepoDir = ".";
3159
+ zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase);
3160
+ fossil_free(zNewBase);
3161
+ blob_init(&cmd, 0, 0);
3162
+ blob_append_escaped_arg(&cmd, g.nameOfExe);
3163
+ blob_append(&cmd, " clone", -1);
3164
+ blob_append_escaped_arg(&cmd, zUri);
3165
+ blob_append_escaped_arg(&cmd, zRepo);
3166
+ zCmd = blob_str(&cmd);
3167
+ fossil_print("%s\n", zCmd);
3168
+ rc = fossil_system(zCmd);
3169
+ if( rc ){
3170
+ fossil_fatal("clone of %s failed", zUri);
3171
+ }
3172
+ blob_reset(&cmd);
3173
+ }else if( zRepoDir ){
3174
+ fossil_fatal("the --repodir option only makes sense if the REPOSITORY "
3175
+ "argument is a URI that begins with http:, https:, ssh:, "
3176
+ "or file:");
3177
+ }
3178
+
3179
+ /* If --workdir is specified, change to the requested working directory */
31263180
if( zWorkDir ){
31273181
file_canonical_name(zRepo, &normalizedRepoName, 0);
31283182
zRepo = blob_str(&normalizedRepoName);
31293183
if( file_isdir(zWorkDir, ExtFILE)!=1 ){
31303184
file_mkfolder(zWorkDir, ExtFILE, 0, 0);
31313185
if( file_mkdir(zWorkDir, ExtFILE, 0) ){
3132
- fossil_fatal("cannot create directory %s\n", zWorkDir);
3186
+ fossil_fatal("cannot create directory %s", zWorkDir);
31333187
}
31343188
}
31353189
if( file_chdir(zWorkDir, 0) ){
3136
- fossil_fatal("unable to make %s the working directory\n", zWorkDir);
3190
+ fossil_fatal("unable to make %s the working directory", zWorkDir);
31373191
}
31383192
}
3193
+
31393194
if( !allowNested && db_open_local(0) ){
31403195
fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot);
31413196
}
31423197
db_open_repository(zRepo);
31433198
@@ -3190,12 +3245,12 @@
31903245
** point, this will probably be the setting value from the
31913246
** repository or global configuration databases. */
31923247
g.allowSymlinks = db_get_boolean("allow-symlinks",
31933248
db_allow_symlinks_by_default());
31943249
}
3195
- db_lset("repository", g.argv[2]);
3196
- db_record_repository_filename(g.argv[2]);
3250
+ db_lset("repository", zRepo);
3251
+ db_record_repository_filename(zRepo);
31973252
db_set_checkout(0);
31983253
azNewArgv[0] = g.argv[0];
31993254
g.argv = azNewArgv;
32003255
if( !emptyFlag ){
32013256
g.argc = 3;
32023257
--- src/db.c
+++ src/db.c
@@ -3070,29 +3070,41 @@
3070 }
3071
3072 /*
3073 ** COMMAND: open
3074 **
3075 ** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS?
3076 **
3077 ** Open a connection to the local repository in FILENAME. A checkout
3078 ** for the repository is created with its root at the working directory.
 
3079 ** If VERSION is specified then that version is checked out. Otherwise
3080 ** the latest version is checked out. No files other than "manifest"
3081 ** and "manifest.uuid" are modified if the --keep option is present.
 
 
 
 
 
 
 
 
3082 **
3083 ** Options:
3084 ** --empty Initialize checkout as being empty, but still connected
3085 ** with the local repository. If you commit this checkout,
3086 ** it will become a new "initial" commit in the repository.
 
3087 ** --keep Only modify the manifest and manifest.uuid files
3088 ** --nested Allow opening a repository inside an opened checkout
3089 ** --force-missing Force opening a repository with missing content
 
3090 ** --setmtime Set timestamps of all files to match their SCM-side
3091 ** times (the timestamp of the last checkin which modified
3092 ** them).
3093 ** --workdir DIR Use DIR as the working directory instead of ".".
 
3094 **
3095 ** See also: close
3096 */
3097 void cmd_open(void){
3098 int emptyFlag;
@@ -3102,19 +3114,21 @@
3102 int allowSymlinks;
3103 int setmtimeFlag; /* --setmtime. Set mtimes on files */
3104 static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
3105 const char *zWorkDir; /* --workdir value */
3106 const char *zRepo = 0; /* Name of the repository file */
 
3107 Blob normalizedRepoName; /* Normalized repository filename */
3108
3109 url_proxy_options();
3110 emptyFlag = find_option("empty",0,0)!=0;
3111 keepFlag = find_option("keep",0,0)!=0;
3112 forceMissingFlag = find_option("force-missing",0,0)!=0;
3113 allowNested = find_option("nested",0,0)!=0;
3114 setmtimeFlag = find_option("setmtime",0,0)!=0;
3115 zWorkDir = find_option("workdir",0,1);
 
3116
3117
3118 /* We should be done with options.. */
3119 verify_all_options();
3120
@@ -3121,23 +3135,64 @@
3121 if( g.argc!=3 && g.argc!=4 ){
3122 usage("REPOSITORY-FILENAME ?VERSION?");
3123 }
3124 zRepo = g.argv[2];
3125 blob_init(&normalizedRepoName, 0, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3126 if( zWorkDir ){
3127 file_canonical_name(zRepo, &normalizedRepoName, 0);
3128 zRepo = blob_str(&normalizedRepoName);
3129 if( file_isdir(zWorkDir, ExtFILE)!=1 ){
3130 file_mkfolder(zWorkDir, ExtFILE, 0, 0);
3131 if( file_mkdir(zWorkDir, ExtFILE, 0) ){
3132 fossil_fatal("cannot create directory %s\n", zWorkDir);
3133 }
3134 }
3135 if( file_chdir(zWorkDir, 0) ){
3136 fossil_fatal("unable to make %s the working directory\n", zWorkDir);
3137 }
3138 }
 
3139 if( !allowNested && db_open_local(0) ){
3140 fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot);
3141 }
3142 db_open_repository(zRepo);
3143
@@ -3190,12 +3245,12 @@
3190 ** point, this will probably be the setting value from the
3191 ** repository or global configuration databases. */
3192 g.allowSymlinks = db_get_boolean("allow-symlinks",
3193 db_allow_symlinks_by_default());
3194 }
3195 db_lset("repository", g.argv[2]);
3196 db_record_repository_filename(g.argv[2]);
3197 db_set_checkout(0);
3198 azNewArgv[0] = g.argv[0];
3199 g.argv = azNewArgv;
3200 if( !emptyFlag ){
3201 g.argc = 3;
3202
--- src/db.c
+++ src/db.c
@@ -3070,29 +3070,41 @@
3070 }
3071
3072 /*
3073 ** COMMAND: open
3074 **
3075 ** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS?
3076 **
3077 ** Open a new connection to the repository name REPOSITORY. A checkout
3078 ** for the repository is created with its root at the current working
3079 ** directory, or at some other directory identified by "--workdir DIR".
3080 ** If VERSION is specified then that version is checked out. Otherwise
3081 ** the most recent check-in on the main branch (usually "trunk") is used.
3082 **
3083 ** REPOSITORY is usually a filename for a repository that already exists
3084 ** on the local machine. But REPOSITORY can also be a URI, in which case
3085 ** the URI is first cloned and the clone is opened. The clone will be stored
3086 ** in the current directory, or in an alternative directory specified by
3087 ** the --repodir option.
3088 **
3089 ** No files other than "manifest" and "manifest.uuid" are modified if
3090 ** the --keep option is present.
3091 **
3092 ** Options:
3093 ** --empty Initialize checkout as being empty, but still connected
3094 ** with the local repository. If you commit this checkout,
3095 ** it will become a new "initial" commit in the repository.
3096 ** --force-missing Force opening a repository with missing content
3097 ** --keep Only modify the manifest and manifest.uuid files
3098 ** --nested Allow opening a repository inside an opened checkout
3099 ** --repodir DIR If REPOSITORY is a URI that will be cloned, store
3100 ** the clone in DIR rather than in "."
3101 ** --setmtime Set timestamps of all files to match their SCM-side
3102 ** times (the timestamp of the last checkin which modified
3103 ** them).
3104 ** --workdir DIR Use DIR as the working directory instead of ".". The DIR
3105 ** directory is created if it does not previously exist.
3106 **
3107 ** See also: close
3108 */
3109 void cmd_open(void){
3110 int emptyFlag;
@@ -3102,19 +3114,21 @@
3114 int allowSymlinks;
3115 int setmtimeFlag; /* --setmtime. Set mtimes on files */
3116 static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
3117 const char *zWorkDir; /* --workdir value */
3118 const char *zRepo = 0; /* Name of the repository file */
3119 const char *zRepoDir = 0; /* --repodir value */
3120 Blob normalizedRepoName; /* Normalized repository filename */
3121
3122 url_proxy_options();
3123 emptyFlag = find_option("empty",0,0)!=0;
3124 keepFlag = find_option("keep",0,0)!=0;
3125 forceMissingFlag = find_option("force-missing",0,0)!=0;
3126 allowNested = find_option("nested",0,0)!=0;
3127 setmtimeFlag = find_option("setmtime",0,0)!=0;
3128 zWorkDir = find_option("workdir",0,1);
3129 zRepoDir = find_option("repodir",0,1);
3130
3131
3132 /* We should be done with options.. */
3133 verify_all_options();
3134
@@ -3121,23 +3135,64 @@
3135 if( g.argc!=3 && g.argc!=4 ){
3136 usage("REPOSITORY-FILENAME ?VERSION?");
3137 }
3138 zRepo = g.argv[2];
3139 blob_init(&normalizedRepoName, 0, 0);
3140
3141 /* If REPOSITORY looks like a URI, then try to clone it first */
3142 if( sqlite3_strglob("http://*", zRepo)==0
3143 || sqlite3_strglob("https://*", zRepo)==0
3144 || sqlite3_strglob("ssh:*", zRepo)==0
3145 || sqlite3_strglob("file:*", zRepo)==0
3146 ){
3147 char *zNewBase; /* Base name of the cloned repository file */
3148 const char *zUri; /* URI to clone */
3149 int i; /* Loop counter */
3150 int rc; /* Result code from fossil_system() */
3151 Blob cmd; /* Clone command to be run */
3152 char *zCmd; /* String version of the clone command */
3153
3154 zUri = zRepo;
3155 zNewBase = fossil_strdup(file_tail(zUri));
3156 for(i=(int)strlen(zNewBase)-1; i>1 && zNewBase[i]!='.'; i--){}
3157 if( zNewBase[i]=='.' ) zNewBase[i] = 0;
3158 if( zRepoDir==0 ) zRepoDir = ".";
3159 zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase);
3160 fossil_free(zNewBase);
3161 blob_init(&cmd, 0, 0);
3162 blob_append_escaped_arg(&cmd, g.nameOfExe);
3163 blob_append(&cmd, " clone", -1);
3164 blob_append_escaped_arg(&cmd, zUri);
3165 blob_append_escaped_arg(&cmd, zRepo);
3166 zCmd = blob_str(&cmd);
3167 fossil_print("%s\n", zCmd);
3168 rc = fossil_system(zCmd);
3169 if( rc ){
3170 fossil_fatal("clone of %s failed", zUri);
3171 }
3172 blob_reset(&cmd);
3173 }else if( zRepoDir ){
3174 fossil_fatal("the --repodir option only makes sense if the REPOSITORY "
3175 "argument is a URI that begins with http:, https:, ssh:, "
3176 "or file:");
3177 }
3178
3179 /* If --workdir is specified, change to the requested working directory */
3180 if( zWorkDir ){
3181 file_canonical_name(zRepo, &normalizedRepoName, 0);
3182 zRepo = blob_str(&normalizedRepoName);
3183 if( file_isdir(zWorkDir, ExtFILE)!=1 ){
3184 file_mkfolder(zWorkDir, ExtFILE, 0, 0);
3185 if( file_mkdir(zWorkDir, ExtFILE, 0) ){
3186 fossil_fatal("cannot create directory %s", zWorkDir);
3187 }
3188 }
3189 if( file_chdir(zWorkDir, 0) ){
3190 fossil_fatal("unable to make %s the working directory", zWorkDir);
3191 }
3192 }
3193
3194 if( !allowNested && db_open_local(0) ){
3195 fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot);
3196 }
3197 db_open_repository(zRepo);
3198
@@ -3190,12 +3245,12 @@
3245 ** point, this will probably be the setting value from the
3246 ** repository or global configuration databases. */
3247 g.allowSymlinks = db_get_boolean("allow-symlinks",
3248 db_allow_symlinks_by_default());
3249 }
3250 db_lset("repository", zRepo);
3251 db_record_repository_filename(zRepo);
3252 db_set_checkout(0);
3253 azNewArgv[0] = g.argv[0];
3254 g.argv = azNewArgv;
3255 if( !emptyFlag ){
3256 g.argc = 3;
3257

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button