commit 2ab439478bef81058efc85623de2d92cd8a66c98 from: Stefan Sperling date: Wed Mar 18 16:16:39 2020 UTC move clone code and documentation up so it appears before checkout commit - 68999b92f2fcbce1a9247328552d8d1bdfc45785 commit + 2ab439478bef81058efc85623de2d92cd8a66c98 blob - 1e1b77c6cf533bbe40050e6b6b163c8f9fc7ce76 blob + e81a7765bea1a60e716d5abbdc052d1066a78c27 --- got/got.1 +++ got/got.1 @@ -136,6 +136,63 @@ follows the globbing rules documented in .It Cm im Short alias for .Cm import . +.It Cm clone Oo Fl q Oc Oo Fl v Oc Ar repository-URL Op Ar target-directory +Clone a Git repository at the specified +.Ar repository-URL +into the specified +.Ar target-directory . +If no +.Ar target-directory +is specified the directory name will be derived from the name of the +cloned repository. +.Cm got clone +will refuse to run if the +.Ar target-directory +already exists. +.Pp +The +.Ar repository-URL +specifies a protocol scheme, a server hostname, and a path to the repository +on the server: +.Lk scheme://hostname/path/to/repository +.Pp +The following protocol schemes are supported: +.Bl -tag -width git+ssh +.It git +The Git protocol as implemented by the +.Xr git-daemon 1 +server. +This protocol is discouraged since it supports neither authentication nor +encryption. +.It git+ssh +The Git protocol wrapped in an authenticated and encrypted +.Xr ssh 1 +tunnel. +With this protocol the hostname may contain an embedded username for +.Xr ssh 1 +to use: +.Mt user@hostname +.It ssh +Short alias for git+ssh. +.El +.Pp +The options for +.Cm got clone +are as follows: +.Bl -tag -width Ds +.It Fl q +Suppress progress reporting output. +The same option will be passed to +.Xr ssh 1 +if applicable. +.It Fl v +Increase the verbosity of progress reporting output. +The same option will be passed to +.Xr ssh 1 +if applicable. +Multiple -v options increase the verbosity. +The maximum is 3. +.El .It Cm checkout Oo Fl E Oc Oo Fl b Ar branch Oc Oo Fl c Ar commit Oc Oo Fl p Ar path-prefix Oc Ar repository-path Op Ar work-tree-path Copy files from a repository into a new work tree. Show the status of each affected file, using the following status codes: @@ -200,63 +257,6 @@ will be checked out. .It Cm co Short alias for .Cm checkout . -.It Cm clone Oo Fl q Oc Oo Fl v Oc Ar repository-URL Op Ar target-directory -Clone a Git repository at the specified -.Ar repository-URL -into the specified -.Ar target-directory . -If no -.Ar target-directory -is specified the directory name will be derived from the name of the -cloned repository. -.Cm got clone -will refuse to run if the -.Ar target-directory -already exists. -.Pp -The -.Ar repository-URL -specifies a protocol scheme, a server hostname, and a path to the repository -on the server: -.Lk scheme://hostname/path/to/repository -.Pp -The following protocol schemes are supported: -.Bl -tag -width git+ssh -.It git -The Git protocol as implemented by the -.Xr git-daemon 1 -server. -This protocol is discouraged since it supports neither authentication nor -encryption. -.It git+ssh -The Git protocol wrapped in an authenticated and encrypted -.Xr ssh 1 -tunnel. -With this protocol the hostname may contain an embedded username for -.Xr ssh 1 -to use: -.Mt user@hostname -.It ssh -Short alias for git+ssh. -.El -.Pp -The options for -.Cm got clone -are as follows: -.Bl -tag -width Ds -.It Fl q -Suppress progress reporting output. -The same option will be passed to -.Xr ssh 1 -if applicable. -.It Fl v -Increase the verbosity of progress reporting output. -The same option will be passed to -.Xr ssh 1 -if applicable. -Multiple -v options increase the verbosity. -The maximum is 3. -.El .It Cm update Oo Fl b Ar branch Oc Oo Fl c Ar commit Oc Op Ar path ... Update an existing work tree to a different commit. Show the status of each affected file, using the following status codes: blob - 799c85e6a9ac36118f5cf224443e1b34e1334500 blob + ac737b0b0a501f0e64fcaea4572206a7185e0b3f --- got/got.c +++ got/got.c @@ -85,8 +85,8 @@ struct got_cmd { __dead static void usage(int); __dead static void usage_init(void); __dead static void usage_import(void); -__dead static void usage_checkout(void); __dead static void usage_clone(void); +__dead static void usage_checkout(void); __dead static void usage_update(void); __dead static void usage_log(void); __dead static void usage_diff(void); @@ -138,8 +138,8 @@ static const struct got_error* cmd_cat(int, char *[]) static struct got_cmd got_commands[] = { { "init", cmd_init, usage_init, "in" }, { "import", cmd_import, usage_import, "im" }, - { "checkout", cmd_checkout, usage_checkout, "co" }, { "clone", cmd_clone, usage_clone, "cl" }, + { "checkout", cmd_checkout, usage_checkout, "co" }, { "update", cmd_update, usage_update, "up" }, { "log", cmd_log, usage_log, "" }, { "diff", cmd_diff, usage_diff, "di" }, @@ -811,163 +811,6 @@ usage_clone(void) fprintf(stderr, "usage: %s clone [-q] [-v] repository-url [target-directory]\n", getprogname()); exit(1); -} - -__dead static void -usage_checkout(void) -{ - fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] " - "[-p prefix] repository-path [worktree-path]\n", getprogname()); - exit(1); -} - -static void -show_worktree_base_ref_warning(void) -{ - fprintf(stderr, "%s: warning: could not create a reference " - "to the work tree's base commit; the commit could be " - "garbage-collected by Git; making the repository " - "writable and running 'got update' will prevent this\n", - getprogname()); -} - -struct got_checkout_progress_arg { - const char *worktree_path; - int had_base_commit_ref_error; -}; - -static const struct got_error * -checkout_progress(void *arg, unsigned char status, const char *path) -{ - struct got_checkout_progress_arg *a = arg; - - /* Base commit bump happens silently. */ - if (status == GOT_STATUS_BUMP_BASE) - return NULL; - - if (status == GOT_STATUS_BASE_REF_ERR) { - a->had_base_commit_ref_error = 1; - return NULL; - } - - while (path[0] == '/') - path++; - - printf("%c %s/%s\n", status, a->worktree_path, path); - return NULL; -} - -static const struct got_error * -check_cancelled(void *arg) -{ - if (sigint_received || sigpipe_received) - return got_error(GOT_ERR_CANCELLED); - return NULL; -} - -static const struct got_error * -check_linear_ancestry(struct got_object_id *commit_id, - struct got_object_id *base_commit_id, int allow_forwards_in_time_only, - struct got_repository *repo) -{ - const struct got_error *err = NULL; - struct got_object_id *yca_id; - - err = got_commit_graph_find_youngest_common_ancestor(&yca_id, - commit_id, base_commit_id, repo, check_cancelled, NULL); - if (err) - return err; - - if (yca_id == NULL) - return got_error(GOT_ERR_ANCESTRY); - - /* - * Require a straight line of history between the target commit - * and the work tree's base commit. - * - * Non-linear situations such as this require a rebase: - * - * (commit) D F (base_commit) - * \ / - * C E - * \ / - * B (yca) - * | - * A - * - * 'got update' only handles linear cases: - * Update forwards in time: A (base/yca) - B - C - D (commit) - * Update backwards in time: D (base) - C - B - A (commit/yca) - */ - if (allow_forwards_in_time_only) { - if (got_object_id_cmp(base_commit_id, yca_id) != 0) - return got_error(GOT_ERR_ANCESTRY); - } else if (got_object_id_cmp(commit_id, yca_id) != 0 && - got_object_id_cmp(base_commit_id, yca_id) != 0) - return got_error(GOT_ERR_ANCESTRY); - - free(yca_id); - return NULL; -} - -static const struct got_error * -check_same_branch(struct got_object_id *commit_id, - struct got_reference *head_ref, struct got_object_id *yca_id, - struct got_repository *repo) -{ - const struct got_error *err = NULL; - struct got_commit_graph *graph = NULL; - struct got_object_id *head_commit_id = NULL; - int is_same_branch = 0; - - err = got_ref_resolve(&head_commit_id, repo, head_ref); - if (err) - goto done; - - if (got_object_id_cmp(head_commit_id, commit_id) == 0) { - is_same_branch = 1; - goto done; - } - if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) { - is_same_branch = 1; - goto done; - } - - err = got_commit_graph_open(&graph, "/", 1); - if (err) - goto done; - - err = got_commit_graph_iter_start(graph, head_commit_id, repo, - check_cancelled, NULL); - if (err) - goto done; - - for (;;) { - struct got_object_id *id; - err = got_commit_graph_iter_next(&id, graph, repo, - check_cancelled, NULL); - if (err) { - if (err->code == GOT_ERR_ITER_COMPLETED) - err = NULL; - break; - } - - if (id) { - if (yca_id && got_object_id_cmp(id, yca_id) == 0) - break; - if (got_object_id_cmp(id, commit_id) == 0) { - is_same_branch = 1; - break; - } - } - } -done: - if (graph) - got_commit_graph_close(graph); - free(head_commit_id); - if (!err && !is_same_branch) - err = got_error(GOT_ERR_ANCESTRY); - return err; } struct got_fetch_progress_arg { @@ -1310,6 +1153,163 @@ done: free(gitconfig_path); free(git_url); return error; +} + +__dead static void +usage_checkout(void) +{ + fprintf(stderr, "usage: %s checkout [-E] [-b branch] [-c commit] " + "[-p prefix] repository-path [worktree-path]\n", getprogname()); + exit(1); +} + +static void +show_worktree_base_ref_warning(void) +{ + fprintf(stderr, "%s: warning: could not create a reference " + "to the work tree's base commit; the commit could be " + "garbage-collected by Git; making the repository " + "writable and running 'got update' will prevent this\n", + getprogname()); +} + +struct got_checkout_progress_arg { + const char *worktree_path; + int had_base_commit_ref_error; +}; + +static const struct got_error * +checkout_progress(void *arg, unsigned char status, const char *path) +{ + struct got_checkout_progress_arg *a = arg; + + /* Base commit bump happens silently. */ + if (status == GOT_STATUS_BUMP_BASE) + return NULL; + + if (status == GOT_STATUS_BASE_REF_ERR) { + a->had_base_commit_ref_error = 1; + return NULL; + } + + while (path[0] == '/') + path++; + + printf("%c %s/%s\n", status, a->worktree_path, path); + return NULL; +} + +static const struct got_error * +check_cancelled(void *arg) +{ + if (sigint_received || sigpipe_received) + return got_error(GOT_ERR_CANCELLED); + return NULL; +} + +static const struct got_error * +check_linear_ancestry(struct got_object_id *commit_id, + struct got_object_id *base_commit_id, int allow_forwards_in_time_only, + struct got_repository *repo) +{ + const struct got_error *err = NULL; + struct got_object_id *yca_id; + + err = got_commit_graph_find_youngest_common_ancestor(&yca_id, + commit_id, base_commit_id, repo, check_cancelled, NULL); + if (err) + return err; + + if (yca_id == NULL) + return got_error(GOT_ERR_ANCESTRY); + + /* + * Require a straight line of history between the target commit + * and the work tree's base commit. + * + * Non-linear situations such as this require a rebase: + * + * (commit) D F (base_commit) + * \ / + * C E + * \ / + * B (yca) + * | + * A + * + * 'got update' only handles linear cases: + * Update forwards in time: A (base/yca) - B - C - D (commit) + * Update backwards in time: D (base) - C - B - A (commit/yca) + */ + if (allow_forwards_in_time_only) { + if (got_object_id_cmp(base_commit_id, yca_id) != 0) + return got_error(GOT_ERR_ANCESTRY); + } else if (got_object_id_cmp(commit_id, yca_id) != 0 && + got_object_id_cmp(base_commit_id, yca_id) != 0) + return got_error(GOT_ERR_ANCESTRY); + + free(yca_id); + return NULL; +} + +static const struct got_error * +check_same_branch(struct got_object_id *commit_id, + struct got_reference *head_ref, struct got_object_id *yca_id, + struct got_repository *repo) +{ + const struct got_error *err = NULL; + struct got_commit_graph *graph = NULL; + struct got_object_id *head_commit_id = NULL; + int is_same_branch = 0; + + err = got_ref_resolve(&head_commit_id, repo, head_ref); + if (err) + goto done; + + if (got_object_id_cmp(head_commit_id, commit_id) == 0) { + is_same_branch = 1; + goto done; + } + if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) { + is_same_branch = 1; + goto done; + } + + err = got_commit_graph_open(&graph, "/", 1); + if (err) + goto done; + + err = got_commit_graph_iter_start(graph, head_commit_id, repo, + check_cancelled, NULL); + if (err) + goto done; + + for (;;) { + struct got_object_id *id; + err = got_commit_graph_iter_next(&id, graph, repo, + check_cancelled, NULL); + if (err) { + if (err->code == GOT_ERR_ITER_COMPLETED) + err = NULL; + break; + } + + if (id) { + if (yca_id && got_object_id_cmp(id, yca_id) == 0) + break; + if (got_object_id_cmp(id, commit_id) == 0) { + is_same_branch = 1; + break; + } + } + } +done: + if (graph) + got_commit_graph_close(graph); + free(head_commit_id); + if (!err && !is_same_branch) + err = got_error(GOT_ERR_ANCESTRY); + return err; } static const struct got_error *