Commit Diff


commit - 4b55f4595673e9079751e4fe3365b412972a4c11
commit + aba9c984d1420b77b23320520e70b27ecc83acf7
blob - 8c1fca65ca8a824451198211eb32ae34620b0f8f
blob + 4d887779da3647bcf38fded4c8db1d120fb3aaaf
--- got/Makefile
+++ got/Makefile
@@ -8,7 +8,7 @@ SRCS=		got.c blame.c commit_graph.c delta.c diff.c \
 		object_idset.c object_parse.c opentemp.c path.c pack.c \
 		privsep.c reference.c repository.c sha1.c worktree.c \
 		inflate.c buf.c worklist.c rcsutil.c diff3.c lockfile.c \
-		deflate.c object_create.c gitconfig.c
+		deflate.c object_create.c
 MAN =		${PROG}.1 got-worktree.5 git-repository.5
 
 CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib
blob - 8ff5e0925c6e1d39cd9c22786a291f25996cb555
blob + ba35bfd0b7a92102f226ca60b973aafe8a4083e7
--- got/got.1
+++ got/got.1
@@ -90,7 +90,14 @@ The
 .Cm got import
 command requires the
 .Ev GOT_AUTHOR
-environment variable to be set.
+environment variable to be set,
+unless Git's
+.Dv user.name
+and
+.Dv user.email configuration settings can be
+obtained from the repository's
+.Pa .git/config
+file.
 .Pp
 The options for
 .Cm got import
@@ -690,7 +697,14 @@ The
 .Cm got commit
 command requires the
 .Ev GOT_AUTHOR
-environment variable to be set.
+environment variable to be set,
+unless Git's
+.Dv user.name
+and
+.Dv user.email configuration settings can be
+obtained from the repository's
+.Pa .git/config
+file.
 .Pp
 The options for
 .Cm got commit
@@ -1204,7 +1218,7 @@ The author's name and email address for
 and
 .Cm got import ,
 for example:
-.An Flan Hacker Aq Mt flan_hacker@openbsd.org .
+.Dq An Flan Hacker Aq Mt flan_hacker@openbsd.org .
 Because
 .Xr git 1
 may fail to parse commits without an email address in author data,
@@ -1212,6 +1226,13 @@ may fail to parse commits without an email address in 
 attempts to reject
 .Ev GOT_AUTHOR
 environment variables with a missing email address.
+Git's
+.Dv user.name
+and
+.Dv user.email configuration settings in the repository's
+.Pa .git/config
+file will override the value of
+.Ev GOT_AUTHOR .
 .It Ev VISUAL , EDITOR
 The editor spawned by
 .Cm got commit ,
blob - 4c8177b93f67a544c333230c5af5ce699c45e6ab
blob + 5f3779846731a64630d792b8c821b712956c9160
--- got/got.c
+++ got/got.c
@@ -492,11 +492,21 @@ import_progress(void *arg, const char *path)
 }
 
 static const struct got_error *
-get_author(const char **author)
+get_author(char **author, struct got_repository *repo)
 {
-	const char *got_author;
+	const struct got_error *err = NULL;
+	const char *got_author, *gitconfig_name, *gitconfig_email;
 
 	*author = NULL;
+
+	gitconfig_name = got_repo_get_gitconfig_author_name(repo);
+	gitconfig_email = got_repo_get_gitconfig_author_email(repo);
+	if (gitconfig_name && gitconfig_email) {
+		if (asprintf(author, "%s <%s>",
+		    gitconfig_name, gitconfig_email) == -1)
+			return got_error_from_errno("asprintf");
+		return NULL;
+	}
 
 	got_author = getenv("GOT_AUTHOR");
 	if (got_author == NULL) {
@@ -504,7 +514,9 @@ get_author(const char **author)
 		return got_error(GOT_ERR_COMMIT_NO_AUTHOR);
 	}
 
-	*author = got_author;
+	*author = strdup(got_author);
+	if (*author == NULL)
+		return got_error_from_errno("strdup");
 
 	/*
 	 * Really dumb email address check; we're only doing this to
@@ -512,18 +524,26 @@ get_author(const char **author)
 	 */
 	while (*got_author && *got_author != '<')
 		got_author++;
-	if (*got_author != '<')
-		return got_error(GOT_ERR_COMMIT_NO_EMAIL);
+	if (*got_author != '<') {
+		err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
+		goto done;
+	}
 	while (*got_author && *got_author != '@')
 		got_author++;
-	if (*got_author != '@')
-		return got_error(GOT_ERR_COMMIT_NO_EMAIL);
+	if (*got_author != '@') {
+		err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
+		goto done;
+	}
 	while (*got_author && *got_author != '>')
 		got_author++;
 	if (*got_author != '>')
-		return got_error(GOT_ERR_COMMIT_NO_EMAIL);
-
-	return NULL;
+		err = got_error(GOT_ERR_COMMIT_NO_EMAIL);
+done:
+	if (err) {
+		free(*author);
+		*author = NULL;
+	}
+	return err;
 }
 
 static const struct got_error *
@@ -531,8 +551,7 @@ cmd_import(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
 	char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
-	char *editor = NULL;
-	const char *author;
+	char *editor = NULL, *author = NULL;
 	const char *branch_name = "master";
 	char *refname = NULL, *id_str = NULL;
 	struct got_repository *repo = NULL;
@@ -581,16 +600,13 @@ cmd_import(int argc, char *argv[])
 	argv += optind;
 
 #ifndef PROFILE
-	if (pledge("stdio rpath wpath cpath fattr flock proc exec unveil",
+	if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
+	    "unveil",
 	    NULL) == -1)
 		err(1, "pledge");
 #endif
 	if (argc != 1)
 		usage_import();
-
-	error = get_author(&author);
-	if (error)
-		return error;
 
 	if (repo_path == NULL) {
 		repo_path = getcwd(NULL, 0);
@@ -601,6 +617,10 @@ cmd_import(int argc, char *argv[])
 	error = got_repo_open(&repo, repo_path);
 	if (error)
 		goto done;
+
+	error = get_author(&author, repo);
+	if (error)
+		return error;
 
 	if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
 		error = got_error_from_errno("asprintf");
@@ -684,6 +704,7 @@ done:
 	free(refname);
 	free(new_commit_id);
 	free(id_str);
+	free(author);
 	if (branch_ref)
 		got_ref_close(branch_ref);
 	if (head_ref)
@@ -3659,8 +3680,7 @@ add_tag(struct got_repository *repo, const char *tag_n
 	struct got_object_id *commit_id = NULL, *tag_id = NULL;
 	char *label = NULL, *commit_id_str = NULL;
 	struct got_reference *ref = NULL;
-	char *refname = NULL, *tagmsg = NULL;
-	const char *tagger;
+	char *refname = NULL, *tagmsg = NULL, *tagger = NULL;
 
 	/*
 	 * Don't let the user create a tag named '-'.
@@ -3670,7 +3690,7 @@ add_tag(struct got_repository *repo, const char *tag_n
 	if (tag_name[0] == '-' && tag_name[1] == '\0')
 		return got_error(GOT_ERR_BAD_REF_NAME);
 
-	err = get_author(&tagger);
+	err = get_author(&tagger, repo);
 	if (err)
 		return err;
 
@@ -3733,6 +3753,7 @@ done:
 	free(commit_id_str);
 	free(refname);
 	free(tagmsg);
+	free(tagger);
 	return err;
 }
 
@@ -4384,9 +4405,8 @@ cmd_commit(int argc, char *argv[])
 	char *cwd = NULL, *id_str = NULL;
 	struct got_object_id *id = NULL;
 	const char *logmsg = NULL;
-	const char *author;
-	struct collect_commit_logmsg_arg cl_arg;
-	char *editor = NULL;
+	struct collect_commit_logmsg_arg cl_arg;
+	char *editor = NULL, *author = NULL;
 	int ch, rebase_in_progress, histedit_in_progress;
 	struct got_pathlist_head paths;
 
@@ -4412,10 +4432,6 @@ cmd_commit(int argc, char *argv[])
 	    "unveil", NULL) == -1)
 		err(1, "pledge");
 #endif
-	error = get_author(&author);
-	if (error)
-		return error;
-
 	cwd = getcwd(NULL, 0);
 	if (cwd == NULL) {
 		error = got_error_from_errno("getcwd");
@@ -4442,6 +4458,10 @@ cmd_commit(int argc, char *argv[])
 	if (error != NULL)
 		goto done;
 
+	error = get_author(&author, repo);
+	if (error)
+		return error;
+
 	/*
 	 * unveil(2) traverses exec(2); if an editor is used we have
 	 * to apply unveil after the log message has been written.
@@ -4495,6 +4515,7 @@ done:
 	free(cwd);
 	free(id_str);
 	free(editor);
+	free(author);
 	return error;
 }
 
blob - 97f04731b6ad9e1bccd53406696dab9612512659
blob + f8e9822b47d297dd837ffbfe9f018be2143c3b44
--- include/got_error.h
+++ include/got_error.h
@@ -123,6 +123,7 @@
 #define GOT_ERR_PATCH_CHOICE	107
 #define GOT_ERR_COMMIT_NO_EMAIL	108
 #define GOT_ERR_TAG_EXISTS	109
+#define GOT_ERR_GIT_REPO_FORMAT	110
 
 static const struct got_error {
 	int code;
@@ -252,6 +253,7 @@ static const struct got_error {
 	    "no email address; an email address is required for compatibility "
 	    "with Git" },
 	{ GOT_ERR_TAG_EXISTS,"specified tag already exists" },
+	{ GOT_ERR_GIT_REPO_FORMAT,"unknown git repository format version" },
 };
 
 /*
blob - 16cdca51a48b0e15d4516534fe4efec46fe384af
blob + f2ecc7472a8da5185c853683082cf7806c36d290
--- include/got_repository.h
+++ include/got_repository.h
@@ -31,6 +31,12 @@ const char *got_repo_get_path(struct got_repository *)
  */
 const char *got_repo_get_path_git_dir(struct got_repository *);
 
+/* Obtain the commit author name if parsed from gitconfig, else NULL. */
+const char *got_repo_get_gitconfig_author_name(struct got_repository *);
+
+/* Obtain the commit author email if parsed from gitconfig, else NULL. */
+const char *got_repo_get_gitconfig_author_email(struct got_repository *);
+
 /*
  * Obtain paths to various directories within a repository.
  * The caller must dispose of a path with free(3).
blob - 754cbc5403b1c92e536fca4f373dea3a300c73d4
blob + 1ddfb97d543989190d1cf894c45ed4ed1424f1aa
--- lib/gitconfig.c
+++ lib/gitconfig.c
@@ -307,7 +307,7 @@ conf_parse(struct got_gitconfig *conf, int trans, char
 }
 
 const struct got_error *
-got_gitconfig_open(struct got_gitconfig **conf, const char *gitconfig_path)
+got_gitconfig_open(struct got_gitconfig **conf, int fd)
 {
 	unsigned int i;
 
@@ -318,7 +318,7 @@ got_gitconfig_open(struct got_gitconfig **conf, const 
 	for (i = 0; i < nitems((*conf)->bindings); i++)
 		LIST_INIT(&(*conf)->bindings[i]);
 	TAILQ_INIT(&(*conf)->trans_queue);
-	return got_gitconfig_reinit(*conf, gitconfig_path);
+	return got_gitconfig_reinit(*conf, fd);
 }
 
 static void
@@ -390,23 +390,16 @@ conf_begin(struct got_gitconfig *conf)
 
 /* Open the config file and map it into our address space, then parse it.  */
 const struct got_error *
-got_gitconfig_reinit(struct got_gitconfig *conf, const char *gitconfig_path)
+got_gitconfig_reinit(struct got_gitconfig *conf, int fd)
 {
 	const struct got_error *err = NULL;
-	int	 fd, trans;
+	int	 trans;
 	size_t	 sz;
 	char	*new_conf_addr = 0;
 	struct stat st;
 
-	fd = open(gitconfig_path, O_RDONLY, 0);
-	if (fd == -1) {
-		if (errno != ENOENT)
-			return got_error_from_errno2("open", gitconfig_path);
-		return NULL;
-	}
-
 	if (fstat(fd, &st)) {
-		err = got_error_from_errno2("fstat", gitconfig_path);
+		err = got_error_from_errno("fstat");
 		goto fail;
 	}
 
@@ -421,8 +414,6 @@ got_gitconfig_reinit(struct got_gitconfig *conf, const
 		err = got_error_from_errno("read");
 		goto fail;
 	}
-	close(fd);
-	fd = -1;
 
 	trans = conf_begin(conf);
 
@@ -438,8 +429,6 @@ got_gitconfig_reinit(struct got_gitconfig *conf, const
 
 fail:
 	free(new_conf_addr);
-	if (fd != -1)
-		close(fd);
 	return err;
 }
 
blob - 828787b0e4bac8fc800346d343d9896bcd3e3801
blob + 4d61ab3964722d849b94e3048f1cb6a15d2edc3f
--- lib/got_lib_gitconfig.h
+++ lib/got_lib_gitconfig.h
@@ -39,15 +39,17 @@ struct got_gitconfig_list {
 struct got_gitconfig;
 
 void     got_gitconfig_free_list(struct got_gitconfig_list *);
-struct got_gitconfig_list *got_gitconfig_get_list(struct got_gitconfig *, char *, char *);
+struct got_gitconfig_list *got_gitconfig_get_list(struct got_gitconfig *,
+    char *, char *);
 struct got_gitconfig_list *got_gitconfig_get_tag_list(struct got_gitconfig *, char *);
-int got_gitconfig_get_num(struct got_gitconfig *, char *, char *, int);
-char *got_gitconfig_get_str(struct got_gitconfig *, char *, char *);
-const struct got_error *got_gitconfig_open(struct got_gitconfig **,
-    const char *);
+int got_gitconfig_get_num(struct got_gitconfig *, char *, char *,
+    int);
+char *got_gitconfig_get_str(struct got_gitconfig *, char *,
+    char *);
+const struct got_error *got_gitconfig_open(struct got_gitconfig **, int);
 void got_gitconfig_close(struct got_gitconfig *);
 int      got_gitconfig_match_num(struct got_gitconfig *, char *, char *, int);
-const struct got_error *got_gitconfig_reinit(struct got_gitconfig *, const char *);
+const struct got_error *got_gitconfig_reinit(struct got_gitconfig *, int);
 int      got_gitconfig_remove(struct got_gitconfig *, int, char *, char *);
 int      got_gitconfig_remove_section(struct got_gitconfig *, int, char *);
 int      got_gitconfig_set(struct got_gitconfig *, int, char *, char *, char *, int, int);
blob - 1ad8832b391a09f83bd2201d0c1400a6c47b6f39
blob + f1e7f2ad14684a954b1bc1e9b4a6195cc963c7c3
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -41,6 +41,7 @@
 #define GOT_PROG_READ_BLOB	got-read-blob
 #define GOT_PROG_READ_TAG	got-read-tag
 #define GOT_PROG_READ_PACK	got-read-pack
+#define GOT_PROG_READ_GITCONFIG	got-read-gitconfig
 
 #define GOT_STRINGIFY(x) #x
 #define GOT_STRINGVAL(x) GOT_STRINGIFY(x)
@@ -58,6 +59,8 @@
 	GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_TAG)
 #define GOT_PATH_PROG_READ_PACK \
 	GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_PACK)
+#define GOT_PATH_PROG_READ_GITCONFIG \
+	GOT_STRINGVAL(GOT_LIBEXECDIR) "/" GOT_STRINGVAL(GOT_PROG_READ_GITCONFIG)
 
 struct got_privsep_child {
 	int imsg_fd;
@@ -102,6 +105,14 @@ enum got_imsg_type {
 
 	/* Message sending file descriptor to a temporary file. */
 	GOT_IMSG_TMPFD,
+
+	/* Messages related to gitconfig files. */
+	GOT_IMSG_GITCONFIG_PARSE_REQUEST,
+	GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST,
+	GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST,
+	GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST,
+	GOT_IMSG_GITCONFIG_INT_VAL,
+	GOT_IMSG_GITCONFIG_STR_VAL,
 };
 
 /* Structure for GOT_IMSG_ERROR. */
@@ -264,3 +275,20 @@ const struct got_error *got_privsep_init_pack_child(st
 const struct got_error *got_privsep_send_packed_obj_req(struct imsgbuf *, int,
     struct got_object_id *);
 const struct got_error *got_privsep_send_pack_child_ready(struct imsgbuf *);
+
+const struct got_error *got_privsep_send_gitconfig_parse_req(struct imsgbuf *,
+    int);
+const struct got_error *
+    got_privsep_send_gitconfig_repository_format_version_req(struct imsgbuf *);
+const struct got_error *got_privsep_send_gitconfig_author_name_req(
+    struct imsgbuf *);
+const struct got_error *got_privsep_send_gitconfig_author_email_req(
+    struct imsgbuf *);
+const struct got_error *got_privsep_send_gitconfig_str(struct imsgbuf *,
+    const char *);
+const struct got_error *got_privsep_recv_gitconfig_str(char **,
+    struct imsgbuf *);
+const struct got_error *got_privsep_send_gitconfig_int(struct imsgbuf *, int);
+const struct got_error *got_privsep_recv_gitconfig_int(int *, struct imsgbuf *);
+
+void got_privsep_exec_child(int[2], const char *, const char *);
blob - 23ca2d585f593cbd7176990ce665928bea219089
blob + 71d6c0578341427205b4086dc64fde982ec030e4
--- lib/got_lib_repository.h
+++ lib/got_lib_repository.h
@@ -42,7 +42,9 @@ struct got_repository {
 	struct got_object_cache tagcache;
 
 	/* Settings read from Git configuration files. */
-	struct got_gitconfig *gitconfig;
+	int gitconfig_repository_format_version;
+	char *gitconfig_author_name;
+	char *gitconfig_author_email;
 };
 
 const struct got_error*got_repo_cache_object(struct got_repository *,
blob - e5c043ce567c8eb883622b2a78b71bb9ba64227d
blob + ca9b1470b78030edb637ec17878e01fe010a17b1
--- lib/object.c
+++ lib/object.c
@@ -179,30 +179,6 @@ get_packfile_path(char **path_packfile, struct got_pac
 		return got_error(GOT_ERR_NO_SPACE);
 
 	return NULL;
-}
-
-static void
-exec_privsep_child(int imsg_fds[2], const char *path, const char *repo_path)
-{
-	if (close(imsg_fds[0]) != 0) {
-		fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
-		_exit(1);
-	}
-
-	if (dup2(imsg_fds[1], GOT_IMSG_FD_CHILD) == -1) {
-		fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
-		_exit(1);
-	}
-	if (closefrom(GOT_IMSG_FD_CHILD + 1) == -1) {
-		fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
-		_exit(1);
-	}
-
-	if (execl(path, path, repo_path, (char *)NULL) == -1) {
-		fprintf(stderr, "%s: %s: %s\n", getprogname(), path,
-		    strerror(errno));
-		_exit(1);
-	}
 }
 
 static const struct got_error *
@@ -272,7 +248,7 @@ start_pack_privsep_child(struct got_pack *pack, struct
 		goto done;
 	} else if (pid == 0) {
 		set_max_datasize();
-		exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_PACK,
+		got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_PACK,
 		    pack->path_packfile);
 		/* not reached */
 	}
@@ -392,7 +368,7 @@ read_object_header_privsep(struct got_object **obj, st
 	if (pid == -1)
 		return got_error_from_errno("fork");
 	else if (pid == 0) {
-		exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_OBJECT,
+		got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_OBJECT,
 		    repo->path);
 		/* not reached */
 	}
@@ -557,7 +533,7 @@ read_commit_privsep(struct got_commit_object **commit,
 	if (pid == -1)
 		return got_error_from_errno("fork");
 	else if (pid == 0) {
-		exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_COMMIT,
+		got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_COMMIT,
 		    repo->path);
 		/* not reached */
 	}
@@ -736,7 +712,7 @@ read_tree_privsep(struct got_tree_object **tree, int o
 	if (pid == -1)
 		return got_error_from_errno("fork");
 	else if (pid == 0) {
-		exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_TREE,
+		got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_TREE,
 		    repo->path);
 		/* not reached */
 	}
@@ -957,7 +933,7 @@ read_blob_privsep(uint8_t **outbuf, size_t *size, size
 	if (pid == -1)
 		return got_error_from_errno("fork");
 	else if (pid == 0) {
-		exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_BLOB,
+		got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_BLOB,
 		    repo->path);
 		/* not reached */
 	}
@@ -1293,7 +1269,7 @@ read_tag_privsep(struct got_tag_object **tag, int obj_
 	if (pid == -1)
 		return got_error_from_errno("fork");
 	else if (pid == 0) {
-		exec_privsep_child(imsg_fds, GOT_PATH_PROG_READ_TAG,
+		got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_TAG,
 		    repo->path);
 		/* not reached */
 	}
blob - f1ce37f56adc95e212cca7ec18ec0d69629deac9
blob + f9961bfb6a787156474e30da32622cdb054366e6
--- lib/privsep.c
+++ lib/privsep.c
@@ -1211,11 +1211,152 @@ got_privsep_send_packed_obj_req(struct imsgbuf *ibuf, 
 	    &iobj, sizeof(iobj)) == -1)
 		return got_error_from_errno("imsg_compose "
 		    "PACKED_OBJECT_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_gitconfig_parse_req(struct imsgbuf *ibuf, int fd)
+{
+	const struct got_error *err = NULL;
+
+	if (imsg_compose(ibuf, GOT_IMSG_GITCONFIG_PARSE_REQUEST, 0, 0, fd,
+	    NULL, 0) == -1) {
+		err = got_error_from_errno("imsg_compose "
+		    "GITCONFIG_PARSE_REQUEST");
+		close(fd);
+		return err;
+	}
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_gitconfig_repository_format_version_req(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf,
+	    GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST, 0, 0, -1,
+	    NULL, 0) == -1)
+		return got_error_from_errno("imsg_compose "
+		    "GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_gitconfig_author_name_req(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf,
+	    GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST, 0, 0, -1, NULL, 0) == -1)
+		return got_error_from_errno("imsg_compose "
+		    "GITCONFIG_AUTHOR_NAME_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_gitconfig_author_email_req(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf,
+	    GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST, 0, 0, -1, NULL, 0) == -1)
+		return got_error_from_errno("imsg_compose "
+		    "GITCONFIG_AUTHOR_EMAIL_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_send_gitconfig_str(struct imsgbuf *ibuf, const char *value)
+{
+	size_t len = value ? strlen(value) + 1 : 0;
+
+	if (imsg_compose(ibuf, GOT_IMSG_GITCONFIG_STR_VAL, 0, 0, -1,
+	    value, len) == -1)
+		return got_error_from_errno("imsg_compose GITCONFIG_STR_VAL");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
+got_privsep_recv_gitconfig_str(char **str, struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	struct imsg imsg;
+	size_t datalen;
+	const size_t min_datalen = 0;
+
+	*str = NULL;
+
+	err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen);
+	if (err)
+		return err;
+	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+	switch (imsg.hdr.type) {
+	case GOT_IMSG_GITCONFIG_STR_VAL:
+		if (datalen == 0)
+			break;
+		*str = malloc(datalen);
+		if (*str == NULL) {
+			err = got_error_from_errno("malloc");
+			break;
+		}
+		if (strlcpy(*str, imsg.data, datalen) >= datalen)
+			err = got_error(GOT_ERR_NO_SPACE);
+		break;
+	default:
+		err = got_error(GOT_ERR_PRIVSEP_MSG);
+		break;
+	}
 
+	imsg_free(&imsg);
+	return err;
+}
+
+const struct got_error *
+got_privsep_send_gitconfig_int(struct imsgbuf *ibuf, int value)
+{
+	if (imsg_compose(ibuf, GOT_IMSG_GITCONFIG_INT_VAL, 0, 0, -1,
+	    &value, sizeof(value)) == -1)
+		return got_error_from_errno("imsg_compose GITCONFIG_INT_VAL");
+
 	return flush_imsg(ibuf);
 }
 
 const struct got_error *
+got_privsep_recv_gitconfig_int(int *val, struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	struct imsg imsg;
+	size_t datalen;
+	const size_t min_datalen =
+	    MIN(sizeof(struct got_imsg_error), sizeof(int));
+
+	*val = 0;
+
+	err = got_privsep_recv_imsg(&imsg, ibuf, min_datalen);
+	if (err)
+		return err;
+	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+	switch (imsg.hdr.type) {
+	case GOT_IMSG_GITCONFIG_INT_VAL:
+		if (datalen != sizeof(*val)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		memcpy(val, imsg.data, sizeof(*val));
+		break;
+	default:
+		err = got_error(GOT_ERR_PRIVSEP_MSG);
+		break;
+	}
+
+	imsg_free(&imsg);
+	return err;
+}
+
+const struct got_error *
 got_privsep_unveil_exec_helpers(void)
 {
 	const char *helpers[] = {
@@ -1225,6 +1366,7 @@ got_privsep_unveil_exec_helpers(void)
 	    GOT_PATH_PROG_READ_TREE,
 	    GOT_PATH_PROG_READ_BLOB,
 	    GOT_PATH_PROG_READ_TAG,
+	    GOT_PATH_PROG_READ_GITCONFIG,
 	};
 	int i;
 
@@ -1236,3 +1378,27 @@ got_privsep_unveil_exec_helpers(void)
 
 	return NULL;
 }
+
+void
+got_privsep_exec_child(int imsg_fds[2], const char *path, const char *repo_path)
+{
+	if (close(imsg_fds[0]) != 0) {
+		fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
+		_exit(1);
+	}
+
+	if (dup2(imsg_fds[1], GOT_IMSG_FD_CHILD) == -1) {
+		fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
+		_exit(1);
+	}
+	if (closefrom(GOT_IMSG_FD_CHILD + 1) == -1) {
+		fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
+		_exit(1);
+	}
+
+	if (execl(path, path, repo_path, (char *)NULL) == -1) {
+		fprintf(stderr, "%s: %s: %s\n", getprogname(), path,
+		    strerror(errno));
+		_exit(1);
+	}
+}
blob - dbee69ba57b1497c455672f6741600c07c1f5af1
blob + 454319ad653bfd26ee86f6f74aa4302c19018868
--- lib/repository.c
+++ lib/repository.c
@@ -17,6 +17,7 @@
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/uio.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <sys/syslimits.h>
@@ -56,7 +57,6 @@
 #include "got_lib_sha1.h"
 #include "got_lib_object_cache.h"
 #include "got_lib_repository.h"
-#include "got_lib_gitconfig.h"
 
 #ifndef nitems
 #define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
@@ -88,6 +88,19 @@ got_repo_get_path_git_dir(struct got_repository *repo)
 	return repo->path_git_dir;
 }
 
+const char *
+got_repo_get_gitconfig_author_name(struct got_repository *repo)
+{
+	return repo->gitconfig_author_name;
+}
+
+/* Obtain the commit author email address parsed from gitconfig. */
+const char *
+got_repo_get_gitconfig_author_email(struct got_repository *repo)
+{
+	return repo->gitconfig_author_email;
+}
+
 int
 got_repo_is_bare(struct got_repository *repo)
 {
@@ -333,7 +346,112 @@ done:
 		repo->path = NULL;
 		free(repo->path_git_dir);
 		repo->path_git_dir = NULL;
+	}
+	return err;
+}
+
+static const struct got_error *
+read_gitconfig(struct got_repository *repo)
+{
+	const struct got_error *err = NULL, *child_err = NULL;
+	char *gitconfig_path = NULL;
+	int fd = -1;
+	int imsg_fds[2] = { -1, -1 };
+	pid_t pid;
+	struct imsgbuf *ibuf;
+
+	/* TODO: Read settings from ~/.gitconfig as well? */
+
+	/* Read repository's .git/config file. */
+	err = get_path_gitconfig(&gitconfig_path, repo);
+	if (err)
+		return err;
+
+	fd = open(gitconfig_path, O_RDONLY);
+	if (fd == -1) {
+		if (errno == ENOENT) {
+			free(gitconfig_path);
+			return NULL;
+		}
+		err = got_error_from_errno2("open", gitconfig_path);
+		free(gitconfig_path);
+		return err;
+	}
+
+	ibuf = calloc(1, sizeof(*ibuf));
+	if (ibuf == NULL) {
+		err = got_error_from_errno("calloc");
+		goto done;
+	}
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_fds) == -1) {
+		err = got_error_from_errno("socketpair");
+		goto done;
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		err = got_error_from_errno("fork");
+		goto done;
+	} else if (pid == 0) {
+		got_privsep_exec_child(imsg_fds, GOT_PATH_PROG_READ_GITCONFIG,
+		    repo->path);
+		/* not reached */
+	}
+
+	if (close(imsg_fds[1]) == -1) {
+		err = got_error_from_errno("close");
+		goto done;
 	}
+	imsg_fds[1] = -1;
+	imsg_init(ibuf, imsg_fds[0]);
+
+	err = got_privsep_send_gitconfig_parse_req(ibuf, fd);
+	if (err)
+		goto done;
+	fd = -1;
+
+	err = got_privsep_send_gitconfig_repository_format_version_req(ibuf);
+	if (err)
+		goto done;
+
+	err = got_privsep_recv_gitconfig_int(
+	    &repo->gitconfig_repository_format_version, ibuf);
+	if (err)
+		goto done;
+
+	err = got_privsep_send_gitconfig_author_name_req(ibuf);
+	if (err)
+		goto done;
+
+	err = got_privsep_recv_gitconfig_str(&repo->gitconfig_author_name,
+	    ibuf);
+	if (err)
+		goto done;
+
+	err = got_privsep_send_gitconfig_author_email_req(ibuf);
+	if (err)
+		goto done;
+
+	err = got_privsep_recv_gitconfig_str(&repo->gitconfig_author_email,
+	    ibuf);
+	if (err)
+		goto done;
+
+	imsg_clear(ibuf);
+	err = got_privsep_send_stop(imsg_fds[0]);
+	child_err = got_privsep_wait_for_child(pid);
+	if (child_err && err == NULL)
+		err = child_err;
+done:
+	if (imsg_fds[0] != -1 && close(imsg_fds[0]) == -1 && err == NULL)
+		err = got_error_from_errno("close");
+	if (imsg_fds[1] != -1 && close(imsg_fds[1]) == -1 && err == NULL)
+		err = got_error_from_errno("close");
+	if (fd != -1 && close(fd) == -1 && err == NULL)
+		err = got_error_from_errno2("close", gitconfig_path);
+	free(gitconfig_path);
+	free(ibuf);
 	return err;
 }
 
@@ -342,7 +460,7 @@ got_repo_open(struct got_repository **repop, const cha
 {
 	struct got_repository *repo = NULL;
 	const struct got_error *err = NULL;
-	char *abspath, *gitconfig_path = NULL;
+	char *abspath;
 	int i, tried_root = 0;
 
 	*repop = NULL;
@@ -409,24 +527,17 @@ got_repo_open(struct got_repository **repop, const cha
 		}
 	} while (path);
 
-	err = get_path_gitconfig(&gitconfig_path, repo);
+	err = read_gitconfig(repo);
 	if (err)
 		goto done;
-
-#ifdef notyet
-	err = got_gitconfig_open(&repo->gitconfig, gitconfig_path);
-	if (err)
-		goto done;
-#else
-	repo->gitconfig = NULL;
-#endif
+	if (repo->gitconfig_repository_format_version != 0)
+		err = got_error_path(path, GOT_ERR_GIT_REPO_FORMAT);
 done:
 	if (err)
 		got_repo_close(repo);
 	else
 		*repop = repo;
 	free(abspath);
-	free(gitconfig_path);
 	return err;
 }
 
@@ -470,8 +581,9 @@ got_repo_close(struct got_repository *repo)
 		    err == NULL)
 			err = got_error_from_errno("close");
 	}
-	if (repo->gitconfig)
-		got_gitconfig_close(repo->gitconfig);
+
+	free(repo->gitconfig_author_name);
+	free(repo->gitconfig_author_email);
 	free(repo);
 
 	return err;
blob - f56d5677dd571b5ac1902a52d0f39389be75da6f
blob + a4c900b7f50d8d643524becc373f46951d405243
--- libexec/Makefile
+++ libexec/Makefile
@@ -1,4 +1,4 @@
 SUBDIR = got-read-blob got-read-commit got-read-object got-read-tree \
-	got-read-tag got-read-pack
+	got-read-tag got-read-pack got-read-gitconfig
 
 .include <bsd.subdir.mk>
blob - /dev/null
blob + f49bf06548491001d3b2fbc4da15ac568171dc17 (mode 644)
--- /dev/null
+++ libexec/got-read-gitconfig/Makefile
@@ -0,0 +1,13 @@
+.PATH:${.CURDIR}/../../lib
+
+.include "../../got-version.mk"
+
+PROG=		got-read-gitconfig
+SRCS=		got-read-gitconfig.c error.c inflate.c object_parse.c \
+		path.c privsep.c sha1.c gitconfig.c
+
+CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib
+LDADD = -lutil -lz
+DPADD = ${LIBZ} ${LIBUTIL}
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + c14f37dd21a67e7027328b8a776e99a036726c7c (mode 644)
--- /dev/null
+++ libexec/got-read-gitconfig/got-read-gitconfig.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2019 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/syslimits.h>
+
+#include <stdint.h>
+#include <imsg.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sha1.h>
+#include <zlib.h>
+
+#include "got_error.h"
+#include "got_object.h"
+
+#include "got_lib_delta.h"
+#include "got_lib_object.h"
+#include "got_lib_privsep.h"
+#include "got_lib_gitconfig.h"
+
+static volatile sig_atomic_t sigint_received;
+
+static void
+catch_sigint(int signo)
+{
+	sigint_received = 1;
+}
+
+static const struct got_error *
+gitconfig_num_request(struct imsgbuf *ibuf, struct got_gitconfig *gitconfig,
+    char *section, char *tag, int def)
+{
+	int value;
+
+	if (gitconfig == NULL)
+		return got_error(GOT_ERR_PRIVSEP_MSG);
+
+	value = got_gitconfig_get_num(gitconfig, section, tag, def);
+	return got_privsep_send_gitconfig_int(ibuf, value);
+}
+
+static const struct got_error *
+gitconfig_str_request(struct imsgbuf *ibuf, struct got_gitconfig *gitconfig,
+    char *section, char *tag)
+{
+	char *value;
+
+	if (gitconfig == NULL)
+		return got_error(GOT_ERR_PRIVSEP_MSG);
+
+	value = got_gitconfig_get_str(gitconfig, section, tag);
+	return got_privsep_send_gitconfig_str(ibuf, value);
+}
+
+int
+main(int argc, char *argv[])
+{
+	const struct got_error *err = NULL;
+	struct imsgbuf ibuf;
+	size_t datalen;
+	struct got_gitconfig *gitconfig = NULL;
+#if 0
+	static int attached;
+
+	while (!attached)
+		sleep(1);
+#endif
+	signal(SIGINT, catch_sigint);
+
+	imsg_init(&ibuf, GOT_IMSG_FD_CHILD);
+
+#ifndef PROFILE
+	/* revoke access to most system calls */
+	if (pledge("stdio recvfd", NULL) == -1) {
+		err = got_error_from_errno("pledge");
+		got_privsep_send_error(&ibuf, err);
+		return 1;
+	}
+#endif
+
+	for (;;) {
+		struct imsg imsg;
+
+		memset(&imsg, 0, sizeof(imsg));
+		imsg.fd = -1;
+
+		if (sigint_received) {
+			err = got_error(GOT_ERR_CANCELLED);
+			break;
+		}
+
+		err = got_privsep_recv_imsg(&imsg, &ibuf, 0);
+		if (err) {
+			if (err->code == GOT_ERR_PRIVSEP_PIPE)
+				err = NULL;
+			break;
+		}
+
+		if (imsg.hdr.type == GOT_IMSG_STOP)
+			break;
+
+		switch (imsg.hdr.type) {
+		case GOT_IMSG_GITCONFIG_PARSE_REQUEST:
+			datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+			if (datalen != 0) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				break;
+			}
+			if (imsg.fd == -1){
+				err = got_error(GOT_ERR_PRIVSEP_NO_FD);
+				break;
+			}
+
+			if (gitconfig)
+				got_gitconfig_close(gitconfig);
+			err = got_gitconfig_open(&gitconfig, imsg.fd);
+			break;
+		case GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST:
+			err = gitconfig_num_request(&ibuf, gitconfig, "core",
+			    "repositoryformatversion", 0);
+			break;
+		case GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST:
+			err = gitconfig_str_request(&ibuf, gitconfig, "user",
+			    "name");
+			break;
+		case GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST:
+			err = gitconfig_str_request(&ibuf, gitconfig, "user",
+			    "email");
+			break;
+		default:
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			break;
+		}
+
+		if (imsg.fd != -1) {
+			if (close(imsg.fd) == -1 && err == NULL)
+				err = got_error_from_errno("close");
+		}
+
+		imsg_free(&imsg);
+		if (err)
+			break;
+	}
+
+	imsg_clear(&ibuf);
+	if (err) {
+		if (!sigint_received && err->code != GOT_ERR_PRIVSEP_PIPE) {
+			fprintf(stderr, "%s: %s\n", getprogname(), err->msg);
+			got_privsep_send_error(&ibuf, err);
+		}
+	}
+	if (close(GOT_IMSG_FD_CHILD) != 0 && err == NULL)
+		err = got_error_from_errno("close");
+	return err ? 1 : 0;
+}
blob - 451b7e6f6979220b6f28c544d8bac7eeb6380112
blob + 55340c74b234a5573505c25f1137cc947e5d43d5
--- regress/cmdline/commit.sh
+++ regress/cmdline/commit.sh
@@ -606,7 +606,45 @@ function test_commit_tree_entry_sorting {
 	ret="$?"
 	test_done "$testroot" "$ret"
 }
+
+function test_commit_gitconfig_author {
+	local testroot=`test_init commit_gitconfig_author`
 
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	(cd $testroot/repo && git config user.name 'Flan Luck')
+	(cd $testroot/repo && git config user.email 'flan_luck@openbsd.org')
+
+	echo "modified alpha" > $testroot/wt/alpha
+	(cd $testroot/wt && got commit -m 'test gitconfig author' > /dev/null)
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	(cd $testroot/repo && got log -l1 | grep ^from: > $testroot/stdout)
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "from: Flan Luck <flan_luck@openbsd.org>" \
+		> $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_commit_basic
 run_test test_commit_new_subdir
 run_test test_commit_subdir
@@ -622,3 +660,4 @@ run_test test_commit_selected_paths
 run_test test_commit_outside_refs_heads
 run_test test_commit_no_email
 run_test test_commit_tree_entry_sorting
+run_test test_commit_gitconfig_author
blob - c4fa14f3feb7a9f4a7b01276e5b724b478cab932
blob + 85f9a8965833c7ec25df706646762df09e50327b
--- tog/Makefile
+++ tog/Makefile
@@ -8,7 +8,7 @@ SRCS=		tog.c blame.c commit_graph.c delta.c diff.c \
 		object_idset.c object_parse.c opentemp.c path.c pack.c \
 		privsep.c reference.c repository.c sha1.c worktree.c \
 		utf8.c inflate.c buf.c worklist.c rcsutil.c diff3.c \
-		lockfile.c deflate.c object_create.c gitconfig.c
+		lockfile.c deflate.c object_create.c
 MAN =		${PROG}.1
 
 CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib