/* * Copyright (c) 2017 Martin Pieuchot * Copyright (c) 2018, 2019, 2020 Stefan Sperling * Copyright (c) 2020 Ori Bernstein * * 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 #include #include #include #include static const struct got_error * 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; struct got_repository *repo = NULL; struct got_reference *branch_ref = NULL, *head_ref = NULL; struct got_object_id *new_commit_id = NULL; int ch; struct got_pathlist_head ignores; struct got_pathlist_entry *pe; int preserve_logmsg = 0; TAILQ_INIT(&ignores); while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) { switch (ch) { case 'b': branch_name = optarg; break; case 'm': logmsg = strdup(optarg); if (logmsg == NULL) { error = got_error_from_errno("strdup"); goto done; } break; case 'r': repo_path = realpath(optarg, NULL); if (repo_path == NULL) { error = got_error_from_errno2("realpath", optarg); goto done; } break; case 'I': if (optarg[0] == '\0') break; error = got_pathlist_insert(&pe, &ignores, optarg, NULL); if (error) goto done; break; default: usage_import(); /* NOTREACHED */ } } #ifndef PROFILE if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " "unveil", NULL) == -1) err(1, "pledge"); #endif if (argc != 1) usage_import(); if (repo_path == NULL) { repo_path = getcwd(NULL, 0); if (repo_path == NULL) return got_error_from_errno("getcwd"); } error = get_gitconfig_path(&gitconfig_path); if (error) goto done; error = got_repo_open(&repo, repo_path, gitconfig_path); if (error) goto done; error = get_author(&author, repo, NULL); if (error) return error; /* * Don't let the user create a branch name with a leading '-'. * While technically a valid reference name, this case is usually * an unintended typo. */ if (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"); goto done; } error = got_ref_open(&branch_ref, repo, refname, 0); if (error) { if (error->code != GOT_ERR_NOT_REF) goto done; } else { error = got_error_msg(GOT_ERR_BRANCH_EXISTS, "import target branch already exists"); goto done; } path_dir = realpath(argv[0], NULL); if (path_dir == NULL) { error = got_error_from_errno2("realpath", argv[0]); goto done; } got_path_strip_trailing_slashes(path_dir); /* * unveil(2) traverses exec(2); if an editor is used we have * to apply unveil after the log message has been written. */ if (logmsg == NULL || strlen(logmsg) == 0) { error = get_editor(&editor); if (error) goto done; free(logmsg); error = collect_import_msg(&logmsg, &logmsg_path, editor, path_dir, refname); if (error) { if (error->code != GOT_ERR_COMMIT_MSG_EMPTY && logmsg_path != NULL) preserve_logmsg = 1; goto done; } } if (unveil(path_dir, "r") != 0) { error = got_error_from_errno2("unveil", path_dir); if (logmsg_path) preserve_logmsg = 1; goto done; } error = apply_unveil(got_repo_get_path(repo), 0, NULL); if (error) { if (logmsg_path) preserve_logmsg = 1; goto done; } error = got_repo_import(&new_commit_id, path_dir, logmsg, author, &ignores, repo, import_progress, NULL); if (error) { if (logmsg_path) preserve_logmsg = 1; goto done; } error = got_ref_alloc(&branch_ref, refname, new_commit_id); if (error) { if (logmsg_path) preserve_logmsg = 1; goto done; } error = got_ref_write(branch_ref, repo); if (error) { if (logmsg_path) preserve_logmsg = 1; goto done; } error = got_object_id_str(&id_str, new_commit_id); if (error) { if (logmsg_path) preserve_logmsg = 1; goto done; } error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0); if (error) { if (error->code != GOT_ERR_NOT_REF) { if (logmsg_path) preserve_logmsg = 1; goto done; } error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD, branch_ref); if (error) { if (logmsg_path) preserve_logmsg = 1; goto done; } error = got_ref_write(head_ref, repo); if (error) { if (logmsg_path) preserve_logmsg = 1; goto done; } } printf("Created branch %s with commit %s\n", got_ref_get_name(branch_ref), id_str); done: if (preserve_logmsg) { fprintf(stderr, "%s: log message preserved in %s\n", getprogname(), logmsg_path); } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL) error = got_error_from_errno2("unlink", logmsg_path); free(logmsg); free(logmsg_path); free(repo_path); free(editor); free(refname); free(new_commit_id); free(id_str); free(author); free(gitconfig_path); if (branch_ref) got_ref_close(branch_ref); if (head_ref) got_ref_close(head_ref); return error; } __dead static void usage_clone(void) { fprintf(stderr, "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] " "[-R reference] repository-url [directory]\n", getprogname()); exit(1); } static const struct got_error * cmd_clone(int argc, char *argv[]) { const struct got_error *error = NULL; const char *uri, *dirname; char *proto, *host, *port, *repo_name, *server_path; char *default_destdir = NULL, *id_str = NULL; const char *repo_path, *remote_repo_path; struct got_repository *repo = NULL; struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs; struct got_pathlist_entry *pe; struct got_object_id *pack_hash = NULL; int ch, fetchfd = -1, fetchstatus; pid_t fetchpid = -1; x /* Create got.conf(5). */ gotconfig_path = got_repo_get_path_gotconfig(repo); if (gotconfig_path == NULL) { error = got_error_from_errno("got_repo_get_path_gotconfig"); goto done; } gotconfig_file = fopen(gotconfig_path, "a"); if (gotconfig_file == NULL) { error = got_error_from_errno2("fopen", gotconfig_path); goto done; } got_path_strip_trailing_slashes(server_path); remote_repo_path = server_path; while (remote_repo_path[0] == '/') remote_repo_path++; if (asprintf(&gotconfig, "remote \"%s\" {\n" "\tserver %s\n" "\tprotocol %s\n" "%s%s%s" "\trepository \"%s\"\n" "%s" "}\n", GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto, port ? "\tport " : "", port ? port : "", port ? "\n" : "", remote_repo_path, mirror_references ? "\tmirror-references yes\n" : "") == -1) { error = got_error_from_errno("asprintf"); goto done; } n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file); if (n != strlen(gotconfig)) { error = got_ferror(gotconfig_file, GOT_ERR_IO); goto done; } }