"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Mark Jamsek <mark@jamsek.com>
Subject:
got: gotadmin init -b to specify HEAD + new got import behaviour
To:
Game of Trees <gameoftrees@openbsd.org>
Date:
Sat, 3 Sep 2022 22:59:28 +1000

Download raw body.

Thread
This is a reworked diff that changes the behaviour of both `gotadmin
init` and `got import` that was briefly discussed with stsp in person.
It enables creating an arbitrary HEAD reference at repo creation with
`gotadmin init`. We also no longer hardcode "main" in `got import` but
instead read HEAD so if a branch is not specified with -b, import
defaults to the branch resolved via the repository's HEAD reference.

This fixes the following issue:

$ gotadmin init new.git
$ got import -r new.git/ -b trunk -m first ~/tmp/import
A  /home/mark/tmp/import/node.c
Created branch refs/heads/trunk with commit 5439199efff5d1c804c65795aa7b3087bca5ceb5
$ got co new.git/
got: reference refs/heads/main not found
$ got co -b trunk new.git/
A  /home/mark/tmp/new/node.c
Checked out refs/heads/trunk: 5a183bd364420d7a1f2645d6c01537052ca3fe74
Now shut up and hack
$ cd new
$ got log
got: reference refs/heads/main not found

With the below diff, the desired branch corresponding to the new
repository's HEAD reference is specified with the new -b option to
`gotadmin init`, which `got import` uses by default instead of "main"
(unless another branch is specified with `got import -b <branch>`).

$ gotadmin init -b trunk new.git
$ got import -r new.git/ -m first ~/tmp/import
A  /home/mark/tmp/import/node.c
Created branch refs/heads/trunk with commit fb72785ae57af6bc47f7ce954c658ac23a527112
$ got co new.git/
A  /home/mark/tmp/new/node.c
Checked out refs/heads/trunk: fb72785ae57af6bc47f7ce954c658ac23a527112
Now shut up and hack
$ cd new
$ got log
-----------------------------------------------
commit 3b55a7d421c15404934c2fd5e1893938133aa368 (trunk)
from: Mark Jamsek <mark@jamsek.dev>
date: Fri Sep  2 19:06:53 2022 UTC

 first

As stsp noted, we need to establish a set of rules under which it is
safe to modify HEAD because Git uses it. The one time we are assuredly
safe to do this is when we use Got to create a new repository;
therefore, it makes sense to write HEAD with `gotadmin init` and read
HEAD with `got import -b` because import can be used with established
repositories--not only new ones.

In brief, an arbitrary HEAD ref can be specified at repository creation
with `gotadmin init -b <branch>`; and `got import` now imports to:

  1. <branch> if specified with `got import -b <branch>`
  2. the branch corresponding to HEAD if invoked without -b
  3. "main" if HEAD cannot be determined

diff refs/heads/main refs/heads/dev/gotadmin
commit - b94206d0acc1c55bad1233c35f959fa7c4af297b
commit + dfc0d48f825ddab566d7fecc493127650e6ad648
blob - bdf06b70325f2720ec4a7f3bcdc35c740b1052f2
blob + 6178e14a56844dbd20c8a9401952e50e1225ddf8
--- got/got.1
+++ got/got.1
@@ -104,12 +104,11 @@ are as follows:
 .Bl -tag -width Ds
 .It Fl b Ar branch
 Create the specified
-.Ar branch
-instead of creating the default branch
-.Dq main .
-Use of this option is required if the
-.Dq main
-branch already exists.
+.Ar branch .
+If this option is not specified, a branch corresponding to the repository's
+HEAD reference will be used.
+Use of this option is required if the branch resolved via the repository's
+HEAD reference already exists.
 .It Fl I Ar pattern
 Ignore files or directories with a name which matches the specified
 .Ar pattern .
blob - 64ef5fc0ef4b1bd3dce557f8ecb20c5f2946dee6
blob + 68fddbdd014caa706fb0574e432c2f0262af9005
--- got/got.c
+++ got/got.c
@@ -759,12 +759,13 @@ cmd_import(int argc, char *argv[])
 	const struct got_error *error = NULL;
 	char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
 	char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
-	const char *branch_name = "main";
-	char *refname = NULL, *id_str = NULL, *logmsg_path = NULL;
+	const char *branch_name = NULL;
+	char *id_str = NULL, *logmsg_path = NULL;
+	char refname[PATH_MAX] = "refs/heads/";
 	struct got_repository *repo = NULL;
 	struct got_reference *branch_ref = NULL, *head_ref = NULL;
 	struct got_object_id *new_commit_id = NULL;
-	int ch;
+	int ch, n = 0;
 	struct got_pathlist_head ignores;
 	struct got_pathlist_entry *pe;
 	int preserve_logmsg = 0;
@@ -843,12 +844,23 @@ cmd_import(int argc, char *argv[])
 	 * While technically a valid reference name, this case is usually
 	 * an unintended typo.
 	 */
-	if (branch_name[0] == '-')
+	if (branch_name && branch_name[0] == '-')
 		return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
 
-	if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
-		error = got_error_from_errno("asprintf");
+	error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
+	if (error && error->code != GOT_ERR_NOT_REF)
 		goto done;
+
+	if (branch_name)
+		n = strlcat(refname, branch_name, sizeof(refname));
+	else if (head_ref)
+		n = strlcpy(refname, got_ref_get_symref_target(head_ref),
+		    sizeof(refname));
+	else
+		n = strlcat(refname, "main", sizeof(refname));
+	if (n >= sizeof(refname)) {
+		error = got_error(GOT_ERR_NO_SPACE);
+		goto done;
 	}
 
 	error = got_ref_open(&branch_ref, repo, refname, 0);
@@ -972,7 +984,6 @@ done:
 	free(logmsg_path);
 	free(repo_path);
 	free(editor);
-	free(refname);
 	free(new_commit_id);
 	free(id_str);
 	free(author);
@@ -1689,7 +1700,7 @@ cmd_clone(int argc, char *argv[])
 		goto done;
 
 	if (!list_refs_only) {
-		error = got_repo_init(repo_path);
+		error = got_repo_init(repo_path, NULL);
 		if (error)
 			goto done;
 		error = got_repo_pack_fds_open(&pack_fds);
blob - 80901f0e63c615048925c439ab54ddeb1f70b743
blob + c633b3177963aca3fa00f1a3474129c3b4441c1b
--- gotadmin/gotadmin.1
+++ gotadmin/gotadmin.1
@@ -53,7 +53,7 @@ The commands for
 .Nm
 are as follows:
 .Bl -tag -width checkout
-.It Cm init Ar repository-path
+.It Cm init Oo Fl b Ar branch Oc Ar repository-path
 Create a new empty repository at the specified
 .Ar repository-path .
 .Pp
@@ -64,6 +64,17 @@ the
 command must be used to populate the empty repository before
 .Cm got checkout
 can be used.
+.Pp
+The options for
+.Cm gotadmin init
+are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar branch
+Make the repository's HEAD reference point to the specified
+.Ar branch
+instead of the default branch
+.Dq main .
+.El
 .It Cm info Op Fl r Ar repository-path
 Display information about a repository.
 This includes some configuration settings from
blob - a378b760fc6dade37673aee3debd9a72eddce03a
blob + f5d9e0890948b0b685001b3dbdc48587c69d9078
--- gotadmin/gotadmin.c
+++ gotadmin/gotadmin.c
@@ -279,11 +279,15 @@ static const struct got_error *
 cmd_init(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
+	const char *head_name = NULL;
 	char *repo_path = NULL;
 	int ch;
 
-	while ((ch = getopt(argc, argv, "")) != -1) {
+	while ((ch = getopt(argc, argv, "b:")) != -1) {
 		switch (ch) {
+		case 'b':
+			head_name = optarg;
+			break;
 		default:
 			usage_init();
 			/* NOTREACHED */
@@ -315,7 +319,7 @@ cmd_init(int argc, char *argv[])
 	if (error)
 		goto done;
 
-	error = got_repo_init(repo_path);
+	error = got_repo_init(repo_path, head_name);
 done:
 	free(repo_path);
 	return error;
blob - dea6dd81d267dfa92571a33f5c7559726bab8d8b
blob + 4f8b24e0e19f1af4e21707388cf9bbf9bb54cd18
--- include/got_repository.h
+++ include/got_repository.h
@@ -130,8 +130,11 @@ int got_repo_is_bare(struct got_repository *);
 const struct got_error *got_repo_map_path(char **, struct got_repository *,
     const char *);
 
-/* Create a new repository in an empty directory at a specified path. */
-const struct got_error *got_repo_init(const char *);
+/*
+ * Create a new repository with optional specified
+ * HEAD ref in an empty directory at a specified path.
+ */
+const struct got_error *got_repo_init(const char *, const char *);
 
 /* Attempt to find a unique object ID for a given ID string prefix. */
 const struct got_error *got_repo_match_object_id_prefix(struct got_object_id **,
blob - 9d9dfd0586d7bfc07846c61a82e2f1778968d95c
blob + 6eea4614aa8373e12a0b8f5dd17f49407071789c
--- lib/repository.c
+++ lib/repository.c
@@ -1560,7 +1560,7 @@ got_repo_unpin_pack(struct got_repository *repo)
 }
 
 const struct got_error *
-got_repo_init(const char *repo_path)
+got_repo_init(const char *repo_path, const char *head_name)
 {
 	const struct got_error *err = NULL;
 	const char *dirnames[] = {
@@ -1570,12 +1570,12 @@ got_repo_init(const char *repo_path)
 	};
 	const char *description_str = "Unnamed repository; "
 	    "edit this file 'description' to name the repository.";
-	const char *headref_str = "ref: refs/heads/main";
+	const char *headref = "ref: refs/heads/";
 	const char *gitconfig_str = "[core]\n"
 	    "\trepositoryformatversion = 0\n"
 	    "\tfilemode = true\n"
 	    "\tbare = true\n";
-	char *path;
+	char *headref_str, *path;
 	size_t i;
 
 	if (!got_path_dir_is_empty(repo_path))
@@ -1600,7 +1600,13 @@ got_repo_init(const char *repo_path)
 
 	if (asprintf(&path, "%s/%s", repo_path, GOT_HEAD_FILE) == -1)
 		return got_error_from_errno("asprintf");
+	if (asprintf(&headref_str, "%s%s", headref,
+	    head_name ? head_name : "main") == -1) {
+		free(path);
+		return got_error_from_errno("asprintf");
+	}
 	err = got_path_create_file(path, headref_str);
+	free(headref_str);
 	free(path);
 	if (err)
 		return err;

-- 
Mark Jamsek <fnc.bsdbox.org>
GPG: F2FF 13DE 6A06 C471 CA80  E6E2 2930 DC66 86EE CF68