Commit Diff


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 *