commit 8069f63672650a2836d923d8f3889829ac63b04c from: Stefan Sperling date: Sat Jan 12 18:28:48 2019 UTC implement -c option for 'got checkout' commit - 27793341b019f82d415b7a38f106a5424a3d2ecb commit + 8069f63672650a2836d923d8f3889829ac63b04c blob - 5e8952c6e1329f6cee0405f65332a159c2395f7b blob + 38b8a369d61e06753db3aee9b76c72215df857ae --- got/got.1 +++ got/got.1 @@ -68,7 +68,7 @@ The commands for .Nm are as follows: .Bl -tag -width checkout -.It Cm checkout [ Fl p Ar path-prefix ] repository-path [ work-tree-path ] +.It Cm checkout [ Fl c Ar commit ] [ Fl p Ar path-prefix ] repository-path [ work-tree-path ] Copy files from a repository into a new work tree. If the .Ar work tree path @@ -83,6 +83,10 @@ The options for .Cm got checkout are as follows: .Bl -tag -width Ds +.It Fl c Ar commit +Check out files from the specified +.Ar commit . +By default, the HEAD commit is used. .It Fl p Ar path-prefix Restrict the work tree to a subset of the repository's tree hierarchy. Only files beneath the specified blob - 9344bf93767b2a5453c8f923e3ee059ef601e562 blob + cabd57bcdcc7027bcfd2382910f6e65e30e1208f --- got/got.c +++ got/got.c @@ -223,8 +223,67 @@ checkout_cancel(void *arg) if (sigint_received || sigpipe_received) return got_error(GOT_ERR_CANCELLED); return NULL; +} + +static const struct got_error * +check_ancestry(struct got_worktree *worktree, struct got_object_id *commit_id, + struct got_repository *repo) +{ + const struct got_error *err; + struct got_reference *head_ref = NULL; + struct got_object_id *head_commit_id = NULL; + struct got_commit_graph *graph = NULL; + + head_ref = got_worktree_get_head_ref(worktree); + if (head_ref == NULL) + return got_error_from_errno(); + + /* TODO: Check the reflog. The head ref may have been rebased. */ + err = got_ref_resolve(&head_commit_id, repo, head_ref); + if (err) + goto done; + + err = got_commit_graph_open(&graph, head_commit_id, "/", 1, repo); + if (err) + goto done; + + err = got_commit_graph_iter_start(graph, head_commit_id, repo); + if (err) + goto done; + while (1) { + struct got_object_id *id; + + if (sigint_received || sigpipe_received) + break; + + err = got_commit_graph_iter_next(&id, graph); + if (err) { + if (err->code == GOT_ERR_ITER_COMPLETED) { + err = got_error(GOT_ERR_ANCESTRY); + break; + } + if (err->code != GOT_ERR_ITER_NEED_MORE) + break; + err = got_commit_graph_fetch_commits(graph, 1, repo); + if (err) + break; + else + continue; + } + if (id == NULL) + break; + if (got_object_id_cmp(id, commit_id) == 0) + break; + } +done: + if (head_ref) + got_ref_close(head_ref); + if (graph) + got_commit_graph_close(graph); + return err; } + static const struct got_error * cmd_checkout(int argc, char *argv[]) { @@ -235,10 +294,16 @@ cmd_checkout(int argc, char *argv[]) char *repo_path = NULL; char *worktree_path = NULL; const char *path_prefix = ""; + char *commit_id_str = NULL; int ch, same_path_prefix; - while ((ch = getopt(argc, argv, "p:")) != -1) { + while ((ch = getopt(argc, argv, "c:p:")) != -1) { switch (ch) { + case 'c': + commit_id_str = strdup(optarg); + if (commit_id_str == NULL) + return got_error_from_errno(); + break; case 'p': path_prefix = optarg; break; @@ -304,6 +369,7 @@ cmd_checkout(int argc, char *argv[]) error = got_repo_open(&repo, repo_path); if (error != NULL) goto done; + error = got_ref_open(&head_ref, repo, GOT_REF_HEAD); if (error != NULL) goto done; @@ -325,6 +391,24 @@ cmd_checkout(int argc, char *argv[]) goto done; } + if (commit_id_str) { + struct got_object_id *commit_id; + error = got_object_resolve_id_str(&commit_id, repo, + commit_id_str); + if (error != NULL) + goto done; + error = check_ancestry(worktree, commit_id, repo); + if (error != NULL) { + free(commit_id); + goto done; + } + error = got_worktree_set_base_commit_id(worktree, repo, + commit_id); + free(commit_id); + if (error) + goto done; + } + error = got_worktree_checkout_files(worktree, repo, checkout_progress, worktree_path, checkout_cancel, NULL); if (error != NULL) @@ -333,6 +417,7 @@ cmd_checkout(int argc, char *argv[]) printf("Now shut up and hack\n"); done: + free(commit_id_str); free(repo_path); free(worktree_path); return error; @@ -355,64 +440,6 @@ update_progress(void *arg, unsigned char status, const while (path[0] == '/') path++; printf("%c %s\n", status, path); -} - -static const struct got_error * -check_ancestry(struct got_worktree *worktree, struct got_object_id *commit_id, - struct got_repository *repo) -{ - const struct got_error *err; - struct got_reference *head_ref = NULL; - struct got_object_id *head_commit_id = NULL; - struct got_commit_graph *graph = NULL; - - head_ref = got_worktree_get_head_ref(worktree); - if (head_ref == NULL) - return got_error_from_errno(); - - /* TODO: Check the reflog. The head ref may have been rebased. */ - err = got_ref_resolve(&head_commit_id, repo, head_ref); - if (err) - goto done; - - err = got_commit_graph_open(&graph, head_commit_id, "/", 1, repo); - if (err) - goto done; - - err = got_commit_graph_iter_start(graph, head_commit_id, repo); - if (err) - goto done; - while (1) { - struct got_object_id *id; - - if (sigint_received || sigpipe_received) - break; - - err = got_commit_graph_iter_next(&id, graph); - if (err) { - if (err->code == GOT_ERR_ITER_COMPLETED) { - err = got_error(GOT_ERR_ANCESTRY); - break; - } - if (err->code != GOT_ERR_ITER_NEED_MORE) - break; - err = got_commit_graph_fetch_commits(graph, 1, repo); - if (err) - break; - else - continue; - } - if (id == NULL) - break; - if (got_object_id_cmp(id, commit_id) == 0) - break; - } -done: - if (head_ref) - got_ref_close(head_ref); - if (graph) - got_commit_graph_close(graph); - return err; } static const struct got_error *