1*59c8e88eSDag-Erling Smørgrav/* 2*59c8e88eSDag-Erling Smørgrav * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org> 3*59c8e88eSDag-Erling Smørgrav * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org> 4*59c8e88eSDag-Erling Smørgrav * Copyright (c) 2020 Ori Bernstein <ori@openbsd.org> 5*59c8e88eSDag-Erling Smørgrav * 6*59c8e88eSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 7*59c8e88eSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 8*59c8e88eSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 9*59c8e88eSDag-Erling Smørgrav * 10*59c8e88eSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11*59c8e88eSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*59c8e88eSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13*59c8e88eSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*59c8e88eSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*59c8e88eSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*59c8e88eSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*59c8e88eSDag-Erling Smørgrav */ 18*59c8e88eSDag-Erling Smørgrav 19*59c8e88eSDag-Erling Smørgrav#include <sys/queue.h> 20*59c8e88eSDag-Erling Smørgrav#include <sys/types.h> 21*59c8e88eSDag-Erling Smørgrav#include <sys/stat.h> 22*59c8e88eSDag-Erling Smørgrav#include <sys/param.h> 23*59c8e88eSDag-Erling Smørgrav#include <sys/wait.h> 24*59c8e88eSDag-Erling Smørgrav 25*59c8e88eSDag-Erling Smørgravstatic const struct got_error * 26*59c8e88eSDag-Erling Smørgravcmd_import(int argc, char *argv[]) 27*59c8e88eSDag-Erling Smørgrav{ 28*59c8e88eSDag-Erling Smørgrav const struct got_error *error = NULL; 29*59c8e88eSDag-Erling Smørgrav char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL; 30*59c8e88eSDag-Erling Smørgrav char *gitconfig_path = NULL, *editor = NULL, *author = NULL; 31*59c8e88eSDag-Erling Smørgrav const char *branch_name = "main"; 32*59c8e88eSDag-Erling Smørgrav char *refname = NULL, *id_str = NULL, *logmsg_path = NULL; 33*59c8e88eSDag-Erling Smørgrav struct got_repository *repo = NULL; 34*59c8e88eSDag-Erling Smørgrav struct got_reference *branch_ref = NULL, *head_ref = NULL; 35*59c8e88eSDag-Erling Smørgrav struct got_object_id *new_commit_id = NULL; 36*59c8e88eSDag-Erling Smørgrav int ch; 37*59c8e88eSDag-Erling Smørgrav struct got_pathlist_head ignores; 38*59c8e88eSDag-Erling Smørgrav struct got_pathlist_entry *pe; 39*59c8e88eSDag-Erling Smørgrav int preserve_logmsg = 0; 40*59c8e88eSDag-Erling Smørgrav 41*59c8e88eSDag-Erling Smørgrav TAILQ_INIT(&ignores); 42*59c8e88eSDag-Erling Smørgrav 43*59c8e88eSDag-Erling Smørgrav while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) { 44*59c8e88eSDag-Erling Smørgrav switch (ch) { 45*59c8e88eSDag-Erling Smørgrav case 'b': 46*59c8e88eSDag-Erling Smørgrav branch_name = optarg; 47*59c8e88eSDag-Erling Smørgrav break; 48*59c8e88eSDag-Erling Smørgrav case 'm': 49*59c8e88eSDag-Erling Smørgrav logmsg = strdup(optarg); 50*59c8e88eSDag-Erling Smørgrav if (logmsg == NULL) { 51*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno("strdup"); 52*59c8e88eSDag-Erling Smørgrav goto done; 53*59c8e88eSDag-Erling Smørgrav } 54*59c8e88eSDag-Erling Smørgrav break; 55*59c8e88eSDag-Erling Smørgrav case 'r': 56*59c8e88eSDag-Erling Smørgrav repo_path = realpath(optarg, NULL); 57*59c8e88eSDag-Erling Smørgrav if (repo_path == NULL) { 58*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno2("realpath", 59*59c8e88eSDag-Erling Smørgrav optarg); 60*59c8e88eSDag-Erling Smørgrav goto done; 61*59c8e88eSDag-Erling Smørgrav } 62*59c8e88eSDag-Erling Smørgrav break; 63*59c8e88eSDag-Erling Smørgrav case 'I': 64*59c8e88eSDag-Erling Smørgrav if (optarg[0] == '\0') 65*59c8e88eSDag-Erling Smørgrav break; 66*59c8e88eSDag-Erling Smørgrav error = got_pathlist_insert(&pe, &ignores, optarg, 67*59c8e88eSDag-Erling Smørgrav NULL); 68*59c8e88eSDag-Erling Smørgrav if (error) 69*59c8e88eSDag-Erling Smørgrav goto done; 70*59c8e88eSDag-Erling Smørgrav break; 71*59c8e88eSDag-Erling Smørgrav default: 72*59c8e88eSDag-Erling Smørgrav usage_import(); 73*59c8e88eSDag-Erling Smørgrav /* NOTREACHED */ 74*59c8e88eSDag-Erling Smørgrav } 75*59c8e88eSDag-Erling Smørgrav } 76*59c8e88eSDag-Erling Smørgrav 77*59c8e88eSDag-Erling Smørgrav#ifndef PROFILE 78*59c8e88eSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd " 79*59c8e88eSDag-Erling Smørgrav "unveil", 80*59c8e88eSDag-Erling Smørgrav NULL) == -1) 81*59c8e88eSDag-Erling Smørgrav err(1, "pledge"); 82*59c8e88eSDag-Erling Smørgrav#endif 83*59c8e88eSDag-Erling Smørgrav if (argc != 1) 84*59c8e88eSDag-Erling Smørgrav usage_import(); 85*59c8e88eSDag-Erling Smørgrav 86*59c8e88eSDag-Erling Smørgrav if (repo_path == NULL) { 87*59c8e88eSDag-Erling Smørgrav repo_path = getcwd(NULL, 0); 88*59c8e88eSDag-Erling Smørgrav if (repo_path == NULL) 89*59c8e88eSDag-Erling Smørgrav return got_error_from_errno("getcwd"); 90*59c8e88eSDag-Erling Smørgrav } 91*59c8e88eSDag-Erling Smørgrav error = get_gitconfig_path(&gitconfig_path); 92*59c8e88eSDag-Erling Smørgrav if (error) 93*59c8e88eSDag-Erling Smørgrav goto done; 94*59c8e88eSDag-Erling Smørgrav error = got_repo_open(&repo, repo_path, gitconfig_path); 95*59c8e88eSDag-Erling Smørgrav if (error) 96*59c8e88eSDag-Erling Smørgrav goto done; 97*59c8e88eSDag-Erling Smørgrav 98*59c8e88eSDag-Erling Smørgrav error = get_author(&author, repo, NULL); 99*59c8e88eSDag-Erling Smørgrav if (error) 100*59c8e88eSDag-Erling Smørgrav return error; 101*59c8e88eSDag-Erling Smørgrav 102*59c8e88eSDag-Erling Smørgrav /* 103*59c8e88eSDag-Erling Smørgrav * Don't let the user create a branch name with a leading '-'. 104*59c8e88eSDag-Erling Smørgrav * While technically a valid reference name, this case is usually 105*59c8e88eSDag-Erling Smørgrav * an unintended typo. 106*59c8e88eSDag-Erling Smørgrav */ 107*59c8e88eSDag-Erling Smørgrav if (branch_name[0] == '-') 108*59c8e88eSDag-Erling Smørgrav return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS); 109*59c8e88eSDag-Erling Smørgrav 110*59c8e88eSDag-Erling Smørgrav if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) { 111*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno("asprintf"); 112*59c8e88eSDag-Erling Smørgrav goto done; 113*59c8e88eSDag-Erling Smørgrav } 114*59c8e88eSDag-Erling Smørgrav 115*59c8e88eSDag-Erling Smørgrav error = got_ref_open(&branch_ref, repo, refname, 0); 116*59c8e88eSDag-Erling Smørgrav if (error) { 117*59c8e88eSDag-Erling Smørgrav if (error->code != GOT_ERR_NOT_REF) 118*59c8e88eSDag-Erling Smørgrav goto done; 119*59c8e88eSDag-Erling Smørgrav } else { 120*59c8e88eSDag-Erling Smørgrav error = got_error_msg(GOT_ERR_BRANCH_EXISTS, 121*59c8e88eSDag-Erling Smørgrav "import target branch already exists"); 122*59c8e88eSDag-Erling Smørgrav goto done; 123*59c8e88eSDag-Erling Smørgrav } 124*59c8e88eSDag-Erling Smørgrav 125*59c8e88eSDag-Erling Smørgrav path_dir = realpath(argv[0], NULL); 126*59c8e88eSDag-Erling Smørgrav if (path_dir == NULL) { 127*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno2("realpath", argv[0]); 128*59c8e88eSDag-Erling Smørgrav goto done; 129*59c8e88eSDag-Erling Smørgrav } 130*59c8e88eSDag-Erling Smørgrav got_path_strip_trailing_slashes(path_dir); 131*59c8e88eSDag-Erling Smørgrav 132*59c8e88eSDag-Erling Smørgrav /* 133*59c8e88eSDag-Erling Smørgrav * unveil(2) traverses exec(2); if an editor is used we have 134*59c8e88eSDag-Erling Smørgrav * to apply unveil after the log message has been written. 135*59c8e88eSDag-Erling Smørgrav */ 136*59c8e88eSDag-Erling Smørgrav if (logmsg == NULL || strlen(logmsg) == 0) { 137*59c8e88eSDag-Erling Smørgrav error = get_editor(&editor); 138*59c8e88eSDag-Erling Smørgrav if (error) 139*59c8e88eSDag-Erling Smørgrav goto done; 140*59c8e88eSDag-Erling Smørgrav free(logmsg); 141*59c8e88eSDag-Erling Smørgrav error = collect_import_msg(&logmsg, &logmsg_path, editor, 142*59c8e88eSDag-Erling Smørgrav path_dir, refname); 143*59c8e88eSDag-Erling Smørgrav if (error) { 144*59c8e88eSDag-Erling Smørgrav if (error->code != GOT_ERR_COMMIT_MSG_EMPTY && 145*59c8e88eSDag-Erling Smørgrav logmsg_path != NULL) 146*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 147*59c8e88eSDag-Erling Smørgrav goto done; 148*59c8e88eSDag-Erling Smørgrav } 149*59c8e88eSDag-Erling Smørgrav } 150*59c8e88eSDag-Erling Smørgrav 151*59c8e88eSDag-Erling Smørgrav if (unveil(path_dir, "r") != 0) { 152*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno2("unveil", path_dir); 153*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 154*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 155*59c8e88eSDag-Erling Smørgrav goto done; 156*59c8e88eSDag-Erling Smørgrav } 157*59c8e88eSDag-Erling Smørgrav 158*59c8e88eSDag-Erling Smørgrav error = apply_unveil(got_repo_get_path(repo), 0, NULL); 159*59c8e88eSDag-Erling Smørgrav if (error) { 160*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 161*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 162*59c8e88eSDag-Erling Smørgrav goto done; 163*59c8e88eSDag-Erling Smørgrav } 164*59c8e88eSDag-Erling Smørgrav 165*59c8e88eSDag-Erling Smørgrav error = got_repo_import(&new_commit_id, path_dir, logmsg, 166*59c8e88eSDag-Erling Smørgrav author, &ignores, repo, import_progress, NULL); 167*59c8e88eSDag-Erling Smørgrav if (error) { 168*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 169*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 170*59c8e88eSDag-Erling Smørgrav goto done; 171*59c8e88eSDag-Erling Smørgrav } 172*59c8e88eSDag-Erling Smørgrav 173*59c8e88eSDag-Erling Smørgrav error = got_ref_alloc(&branch_ref, refname, new_commit_id); 174*59c8e88eSDag-Erling Smørgrav if (error) { 175*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 176*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 177*59c8e88eSDag-Erling Smørgrav goto done; 178*59c8e88eSDag-Erling Smørgrav } 179*59c8e88eSDag-Erling Smørgrav 180*59c8e88eSDag-Erling Smørgrav error = got_ref_write(branch_ref, repo); 181*59c8e88eSDag-Erling Smørgrav if (error) { 182*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 183*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 184*59c8e88eSDag-Erling Smørgrav goto done; 185*59c8e88eSDag-Erling Smørgrav } 186*59c8e88eSDag-Erling Smørgrav 187*59c8e88eSDag-Erling Smørgrav error = got_object_id_str(&id_str, new_commit_id); 188*59c8e88eSDag-Erling Smørgrav if (error) { 189*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 190*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 191*59c8e88eSDag-Erling Smørgrav goto done; 192*59c8e88eSDag-Erling Smørgrav } 193*59c8e88eSDag-Erling Smørgrav 194*59c8e88eSDag-Erling Smørgrav error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0); 195*59c8e88eSDag-Erling Smørgrav if (error) { 196*59c8e88eSDag-Erling Smørgrav if (error->code != GOT_ERR_NOT_REF) { 197*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 198*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 199*59c8e88eSDag-Erling Smørgrav goto done; 200*59c8e88eSDag-Erling Smørgrav } 201*59c8e88eSDag-Erling Smørgrav 202*59c8e88eSDag-Erling Smørgrav error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD, 203*59c8e88eSDag-Erling Smørgrav branch_ref); 204*59c8e88eSDag-Erling Smørgrav if (error) { 205*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 206*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 207*59c8e88eSDag-Erling Smørgrav goto done; 208*59c8e88eSDag-Erling Smørgrav } 209*59c8e88eSDag-Erling Smørgrav 210*59c8e88eSDag-Erling Smørgrav error = got_ref_write(head_ref, repo); 211*59c8e88eSDag-Erling Smørgrav if (error) { 212*59c8e88eSDag-Erling Smørgrav if (logmsg_path) 213*59c8e88eSDag-Erling Smørgrav preserve_logmsg = 1; 214*59c8e88eSDag-Erling Smørgrav goto done; 215*59c8e88eSDag-Erling Smørgrav } 216*59c8e88eSDag-Erling Smørgrav } 217*59c8e88eSDag-Erling Smørgrav 218*59c8e88eSDag-Erling Smørgrav printf("Created branch %s with commit %s\n", 219*59c8e88eSDag-Erling Smørgrav got_ref_get_name(branch_ref), id_str); 220*59c8e88eSDag-Erling Smørgravdone: 221*59c8e88eSDag-Erling Smørgrav if (preserve_logmsg) { 222*59c8e88eSDag-Erling Smørgrav fprintf(stderr, "%s: log message preserved in %s\n", 223*59c8e88eSDag-Erling Smørgrav getprogname(), logmsg_path); 224*59c8e88eSDag-Erling Smørgrav } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL) 225*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno2("unlink", logmsg_path); 226*59c8e88eSDag-Erling Smørgrav free(logmsg); 227*59c8e88eSDag-Erling Smørgrav free(logmsg_path); 228*59c8e88eSDag-Erling Smørgrav free(repo_path); 229*59c8e88eSDag-Erling Smørgrav free(editor); 230*59c8e88eSDag-Erling Smørgrav free(refname); 231*59c8e88eSDag-Erling Smørgrav free(new_commit_id); 232*59c8e88eSDag-Erling Smørgrav free(id_str); 233*59c8e88eSDag-Erling Smørgrav free(author); 234*59c8e88eSDag-Erling Smørgrav free(gitconfig_path); 235*59c8e88eSDag-Erling Smørgrav if (branch_ref) 236*59c8e88eSDag-Erling Smørgrav got_ref_close(branch_ref); 237*59c8e88eSDag-Erling Smørgrav if (head_ref) 238*59c8e88eSDag-Erling Smørgrav got_ref_close(head_ref); 239*59c8e88eSDag-Erling Smørgrav return error; 240*59c8e88eSDag-Erling Smørgrav} 241*59c8e88eSDag-Erling Smørgrav 242*59c8e88eSDag-Erling Smørgrav__dead static void 243*59c8e88eSDag-Erling Smørgravusage_clone(void) 244*59c8e88eSDag-Erling Smørgrav{ 245*59c8e88eSDag-Erling Smørgrav fprintf(stderr, "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] " 246*59c8e88eSDag-Erling Smørgrav "[-R reference] repository-url [directory]\n", getprogname()); 247*59c8e88eSDag-Erling Smørgrav exit(1); 248*59c8e88eSDag-Erling Smørgrav} 249*59c8e88eSDag-Erling Smørgrav 250*59c8e88eSDag-Erling Smørgravstatic const struct got_error * 251*59c8e88eSDag-Erling Smørgravcmd_clone(int argc, char *argv[]) 252*59c8e88eSDag-Erling Smørgrav{ 253*59c8e88eSDag-Erling Smørgrav const struct got_error *error = NULL; 254*59c8e88eSDag-Erling Smørgrav const char *uri, *dirname; 255*59c8e88eSDag-Erling Smørgrav char *proto, *host, *port, *repo_name, *server_path; 256*59c8e88eSDag-Erling Smørgrav char *default_destdir = NULL, *id_str = NULL; 257*59c8e88eSDag-Erling Smørgrav const char *repo_path; 258*59c8e88eSDag-Erling Smørgrav struct got_repository *repo = NULL; 259*59c8e88eSDag-Erling Smørgrav struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs; 260*59c8e88eSDag-Erling Smørgrav struct got_pathlist_entry *pe; 261*59c8e88eSDag-Erling Smørgrav struct got_object_id *pack_hash = NULL; 262*59c8e88eSDag-Erling Smørgrav int ch, fetchfd = -1, fetchstatus; 263*59c8e88eSDag-Erling Smørgrav pid_t fetchpid = -1; 264*59c8e88eSDag-Erling Smørgrav x 265*59c8e88eSDag-Erling Smørgrav 266*59c8e88eSDag-Erling Smørgrav /* Create got.conf(5). */ 267*59c8e88eSDag-Erling Smørgrav gotconfig_path = got_repo_get_path_gotconfig(repo); 268*59c8e88eSDag-Erling Smørgrav if (gotconfig_path == NULL) { 269*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno("got_repo_get_path_gotconfig"); 270*59c8e88eSDag-Erling Smørgrav goto done; 271*59c8e88eSDag-Erling Smørgrav } 272*59c8e88eSDag-Erling Smørgrav gotconfig_file = fopen(gotconfig_path, "a"); 273*59c8e88eSDag-Erling Smørgrav if (gotconfig_file == NULL) { 274*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno2("fopen", gotconfig_path); 275*59c8e88eSDag-Erling Smørgrav goto done; 276*59c8e88eSDag-Erling Smørgrav } 277*59c8e88eSDag-Erling Smørgrav got_path_strip_trailing_slashes(server_path); 278*59c8e88eSDag-Erling Smørgrav if (asprintf(&gotconfig, 279*59c8e88eSDag-Erling Smørgrav "remote \"%s\" {\n" 280*59c8e88eSDag-Erling Smørgrav "\tserver %s\n" 281*59c8e88eSDag-Erling Smørgrav "\tprotocol %s\n" 282*59c8e88eSDag-Erling Smørgrav "%s%s%s" 283*59c8e88eSDag-Erling Smørgrav "\trepository \"%s\"\n" 284*59c8e88eSDag-Erling Smørgrav "%s" 285*59c8e88eSDag-Erling Smørgrav "}\n", 286*59c8e88eSDag-Erling Smørgrav GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto, 287*59c8e88eSDag-Erling Smørgrav port ? "\tport " : "", port ? port : "", port ? "\n" : "", 288*59c8e88eSDag-Erling Smørgrav server_path, 289*59c8e88eSDag-Erling Smørgrav mirror_references ? "\tmirror-references yes\n" : "") == -1) { 290*59c8e88eSDag-Erling Smørgrav error = got_error_from_errno("asprintf"); 291*59c8e88eSDag-Erling Smørgrav goto done; 292*59c8e88eSDag-Erling Smørgrav } 293*59c8e88eSDag-Erling Smørgrav n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file); 294*59c8e88eSDag-Erling Smørgrav if (n != strlen(gotconfig)) { 295*59c8e88eSDag-Erling Smørgrav error = got_ferror(gotconfig_file, GOT_ERR_IO); 296*59c8e88eSDag-Erling Smørgrav goto done; 297*59c8e88eSDag-Erling Smørgrav } 298*59c8e88eSDag-Erling Smørgrav} 299