int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; struct strbuf buf = STRBUF_INIT; const char *head_arg; int flag, head_invalid = 0, i; int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; struct commit_list *common = NULL; const char *best_strategy = NULL, *wt_strategy = NULL; struct commit_list **remotes = &remoteheads; if (file_exists(git_path("MERGE_HEAD"))) die("You have not concluded your merge. (MERGE_HEAD exists)"); if (read_cache_unmerged()) die("You are in the middle of a conflicted merge." " (index unmerged)"); /* * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ branch = resolve_ref("HEAD", head, 0, &flag); if (branch && !prefixcmp(branch, "refs/heads/")) branch += 11; if (is_null_sha1(head)) head_invalid = 1; git_config(git_merge_config, NULL); /* for color.ui */ if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; argc = parse_options(argc, argv, prefix, builtin_merge_options, builtin_merge_usage, 0); if (verbosity < 0) show_diffstat = 0; if (squash) { if (!allow_fast_forward) die("You cannot combine --squash with --no-ff."); option_commit = 0; } if (!allow_fast_forward && fast_forward_only) die("You cannot combine --no-ff with --ff-only."); if (!argc) usage_with_options(builtin_merge_usage, builtin_merge_options); /* * This could be traditional "merge <msg> HEAD <commit>..." and * the way we can tell it is to see if the second token is HEAD, * but some people might have misused the interface and used a * committish that is the same as HEAD there instead. * Traditional format never would have "-m" so it is an * additional safety measure to check for it. */ if (!have_message && is_old_style_invocation(argc, argv)) { strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; argv += 2; argc -= 2; } else if (head_invalid) { struct object *remote_head; /* * If the merged head is a valid one there is no reason * to forbid "git merge" into a branch yet to be born. * We do the same for "git pull". */ if (argc != 1) die("Can merge only exactly one commit into " "empty head"); if (squash) die("Squash commit into empty head not supported yet"); if (!allow_fast_forward) die("Non-fast-forward commit does not make sense into " "an empty head"); remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT); if (!remote_head) die("%s - not something we can merge", argv[0]); update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0, DIE_ON_ERR); reset_hard(remote_head->sha1, 0); return 0; } else { struct strbuf msg = STRBUF_INIT; /* We are invoked directly as the first-class UI. */ head_arg = "HEAD"; /* * All the rest are the commits being merged; * prepare the standard merge summary message to * be appended to the given message. If remote * is invalid we will die later in the common * codepath so we discard the error in this * loop. */ if (!have_message) { for (i = 0; i < argc; i++) merge_name(argv[i], &msg); fmt_merge_msg(option_log, &msg, &merge_msg); if (merge_msg.len) strbuf_setlen(&merge_msg, merge_msg.len-1); } } if (head_invalid || !argc) usage_with_options(builtin_merge_usage, builtin_merge_options); strbuf_addstr(&buf, "merge"); for (i = 0; i < argc; i++) strbuf_addf(&buf, " %s", argv[i]); setenv("GIT_REFLOG_ACTION", buf.buf, 0); strbuf_reset(&buf); for (i = 0; i < argc; i++) { struct object *o; struct commit *commit; o = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT); if (!o) die("%s - not something we can merge", argv[i]); commit = lookup_commit(o->sha1); commit->util = (void *)argv[i]; remotes = &commit_list_insert(commit, remotes)->next; strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1)); setenv(buf.buf, argv[i], 1); strbuf_reset(&buf); } if (!use_strategies) { if (!remoteheads->next) add_strategies(pull_twohead, DEFAULT_TWOHEAD); else add_strategies(pull_octopus, DEFAULT_OCTOPUS); } for (i = 0; i < use_strategies_nr; i++) { if (use_strategies[i]->attr & NO_FAST_FORWARD) allow_fast_forward = 0; if (use_strategies[i]->attr & NO_TRIVIAL) allow_trivial = 0; } if (!remoteheads->next) common = get_merge_bases(lookup_commit(head), remoteheads->item, 1); else { struct commit_list *list = remoteheads; commit_list_insert(lookup_commit(head), &list); common = get_octopus_merge_bases(list); free(list); } update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0, DIE_ON_ERR); if (!common) ; /* No common ancestors found. We need a real merge. */ else if (!remoteheads->next && !common->next && common->item == remoteheads->item) { /* * If head can reach all the merge then we are up to date. * but first the most common case of merging one remote. */ finish_up_to_date("Already up-to-date."); return 0; } else if (allow_fast_forward && !remoteheads->next && !common->next && !hashcmp(common->item->object.sha1, head)) { /* Again the most common case of merging one remote. */ struct strbuf msg = STRBUF_INIT; struct object *o; char hex[41]; strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV)); if (verbosity >= 0) printf("Updating %s..%s\n", hex, find_unique_abbrev(remoteheads->item->object.sha1, DEFAULT_ABBREV)); strbuf_addstr(&msg, "Fast-forward"); if (have_message) strbuf_addstr(&msg, " (no commit created; -m option ignored)"); o = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1), 0, NULL, OBJ_COMMIT); if (!o) return 1; if (checkout_fast_forward(head, remoteheads->item->object.sha1)) return 1; finish(o->sha1, msg.buf); drop_save(); return 0; } else if (!remoteheads->next && common->next) ; /* * We are not doing octopus and not fast-forward. Need * a real merge. */ else if (!remoteheads->next && !common->next && option_commit) { /* * We are not doing octopus, not fast-forward, and have * only one common. */ refresh_cache(REFRESH_QUIET); if (allow_trivial && !fast_forward_only) { /* See if it is really trivial. */ git_committer_info(IDENT_ERROR_ON_NO_NAME); printf("Trying really trivial in-index merge...\n"); if (!read_tree_trivial(common->item->object.sha1, head, remoteheads->item->object.sha1)) return merge_trivial(); printf("Nope.\n"); } } else { /* * An octopus. If we can reach all the remote we are up * to date. */ int up_to_date = 1; struct commit_list *j; for (j = remoteheads; j; j = j->next) { struct commit_list *common_one; /* * Here we *have* to calculate the individual * merge_bases again, otherwise "git merge HEAD^ * HEAD^^" would be missed. */ common_one = get_merge_bases(lookup_commit(head), j->item, 1); if (hashcmp(common_one->item->object.sha1, j->item->object.sha1)) { up_to_date = 0; break; } } if (up_to_date) { finish_up_to_date("Already up-to-date. Yeeah!"); return 0; } } if (fast_forward_only) die("Not possible to fast-forward, aborting."); /* We are going to make a new commit. */ git_committer_info(IDENT_ERROR_ON_NO_NAME); /* * At this point, we need a real merge. No matter what strategy * we use, it would operate on the index, possibly affecting the * working tree, and when resolved cleanly, have the desired * tree in the index -- this means that the index must be in * sync with the head commit. The strategies are responsible * to ensure this. */ if (use_strategies_nr != 1) { /* * Stash away the local changes so that we can try more * than one. */ save_state(); } else { memcpy(stash, null_sha1, 20); } for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { printf("Rewinding the tree to pristine...\n"); restore_state(); } if (use_strategies_nr != 1) printf("Trying merge strategy %s...\n", use_strategies[i]->name); /* * Remember which strategy left the state in the working * tree. */ wt_strategy = use_strategies[i]->name; ret = try_merge_strategy(use_strategies[i]->name, common, head_arg); if (!option_commit && !ret) { merge_was_ok = 1; /* * This is necessary here just to avoid writing * the tree, but later we will *not* exit with * status code 1 because merge_was_ok is set. */ ret = 1; } if (ret) { /* * The backend exits with 1 when conflicts are * left to be resolved, with 2 when it does not * handle the given merge at all. */ if (ret == 1) { int cnt = evaluate_result(); if (best_cnt <= 0 || cnt <= best_cnt) { best_strategy = use_strategies[i]->name; best_cnt = cnt; } } if (merge_was_ok) break; else continue; } /* Automerge succeeded. */ write_tree_trivial(result_tree); automerge_was_ok = 1; break; } /* * If we have a resulting tree, that means the strategy module * auto resolved the merge cleanly. */ if (automerge_was_ok) return finish_automerge(common, result_tree, wt_strategy); /* * Pick the result from the best strategy and have the user fix * it up. */ if (!best_strategy) { restore_state(); if (use_strategies_nr > 1) fprintf(stderr, "No merge strategy handled the merge.\n"); else fprintf(stderr, "Merge with strategy %s failed.\n", use_strategies[0]->name); return 2; } else if (best_strategy == wt_strategy) ; /* We already have its result in the working tree. */ else { printf("Rewinding the tree to pristine...\n"); restore_state(); printf("Using the %s to prepare resolving by hand.\n", best_strategy); try_merge_strategy(best_strategy, common, head_arg); } if (squash) finish(NULL, NULL); else { int fd; struct commit_list *j; for (j = remoteheads; j; j = j->next) strbuf_addf(&buf, "%s\n", sha1_to_hex(j->item->object.sha1)); fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno("Could not open '%s' for writing", git_path("MERGE_HEAD")); if (write_in_full(fd, buf.buf, buf.len) != buf.len) die_errno("Could not write to '%s'", git_path("MERGE_HEAD")); close(fd); strbuf_addch(&merge_msg, '\n'); fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno("Could not open '%s' for writing", git_path("MERGE_MSG")); if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len) die_errno("Could not write to '%s'", git_path("MERGE_MSG")); close(fd); fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) die_errno("Could not open '%s' for writing", git_path("MERGE_MODE")); strbuf_reset(&buf); if (!allow_fast_forward) strbuf_addf(&buf, "no-ff"); if (write_in_full(fd, buf.buf, buf.len) != buf.len) die_errno("Could not write to '%s'", git_path("MERGE_MODE")); close(fd); } if (merge_was_ok) { fprintf(stderr, "Automatic merge went well; " "stopped before committing as requested\n"); return 0; } else return suggest_conflicts(); }
int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0, is_local; struct stat buf; const char *repo_name, *repo, *work_tree, *git_dir; char *path, *dir; int dest_exists; const struct ref *refs, *remote_head; const struct ref *remote_head_points_at; const struct ref *our_head_points_at; struct ref *mapped_refs; const struct ref *ref; struct strbuf key = STRBUF_INIT, value = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; const char *src_ref_prefix = "refs/heads/"; struct remote *remote; int err = 0, complete_refs_before_fetch = 1; struct refspec *refspec; const char *fetch_pattern; junk_pid = getpid(); packet_trace_identity("clone"); argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); if (argc > 2) usage_msg_opt(_("Too many arguments."), builtin_clone_usage, builtin_clone_options); if (argc == 0) usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); if (option_single_branch == -1) option_single_branch = option_depth ? 1 : 0; if (option_mirror) option_bare = 1; if (option_bare) { if (option_origin) die(_("--bare and --origin %s options are incompatible."), option_origin); if (real_git_dir) die(_("--bare and --separate-git-dir are incompatible.")); option_no_checkout = 1; } if (!option_origin) option_origin = "origin"; repo_name = argv[0]; path = get_repo_path(repo_name, &is_bundle); if (path) repo = xstrdup(absolute_path(repo_name)); else if (!strchr(repo_name, ':')) die(_("repository '%s' does not exist"), repo_name); else repo = repo_name; is_local = option_local != 0 && path && !is_bundle; if (is_local && option_depth) warning(_("--depth is ignored in local clones; use file:// instead.")); if (option_local > 0 && !is_local) warning(_("--local is ignored")); if (argc == 2) dir = xstrdup(argv[1]); else dir = guess_dir_name(repo_name, is_bundle, option_bare); strip_trailing_slashes(dir); dest_exists = !stat(dir, &buf); if (dest_exists && !is_empty_dir(dir)) die(_("destination path '%s' already exists and is not " "an empty directory."), dir); strbuf_addf(&reflog_msg, "clone: from %s", repo); if (option_bare) work_tree = NULL; else { work_tree = getenv("GIT_WORK_TREE"); if (work_tree && !stat(work_tree, &buf)) die(_("working tree '%s' already exists."), work_tree); } if (option_bare || work_tree) git_dir = xstrdup(dir); else { work_tree = dir; git_dir = mkpathdup("%s/.git", dir); } if (!option_bare) { junk_work_tree = work_tree; if (safe_create_leading_directories_const(work_tree) < 0) die_errno(_("could not create leading directories of '%s'"), work_tree); if (!dest_exists && mkdir(work_tree, 0777)) die_errno(_("could not create work tree dir '%s'."), work_tree); set_git_work_tree(work_tree); } junk_git_dir = git_dir; atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); set_git_dir_init(git_dir, real_git_dir, 0); if (real_git_dir) { git_dir = real_git_dir; junk_git_dir = real_git_dir; } if (0 <= option_verbosity) { if (option_bare) printf(_("Cloning into bare repository '%s'...\n"), dir); else printf(_("Cloning into '%s'...\n"), dir); } init_db(option_template, INIT_DB_QUIET); write_config(&option_config); git_config(git_default_config, NULL); if (option_bare) { if (option_mirror) src_ref_prefix = "refs/"; strbuf_addstr(&branch_top, src_ref_prefix); git_config_set("core.bare", "true"); } else { strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); } strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); strbuf_addf(&key, "remote.%s.url", option_origin); git_config_set(key.buf, repo); strbuf_reset(&key); if (option_reference.nr) setup_reference(); fetch_pattern = value.buf; refspec = parse_fetch_refspec(1, &fetch_pattern); strbuf_reset(&value); remote = remote_get(option_origin); transport = transport_get(remote, remote->url[0]); if (!is_local) { if (!transport->get_refs_list || !transport->fetch) die(_("Don't know how to clone %s"), transport->url); transport_set_option(transport, TRANS_OPT_KEEP, "yes"); if (option_depth) transport_set_option(transport, TRANS_OPT_DEPTH, option_depth); if (option_single_branch) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); transport_set_verbosity(transport, option_verbosity, option_progress); if (option_upload_pack) transport_set_option(transport, TRANS_OPT_UPLOADPACK, option_upload_pack); if (transport->smart_options && !option_depth) transport->smart_options->check_self_contained_and_connected = 1; } refs = transport_get_remote_refs(transport); if (refs) { mapped_refs = wanted_peer_refs(refs, refspec); /* * transport_get_remote_refs() may return refs with null sha-1 * in mapped_refs (see struct transport->get_refs_list * comment). In that case we need fetch it early because * remote_head code below relies on it. * * for normal clones, transport_get_remote_refs() should * return reliable ref set, we can delay cloning until after * remote HEAD check. */ for (ref = refs; ref; ref = ref->next) if (is_null_sha1(ref->old_sha1)) { complete_refs_before_fetch = 0; break; } if (!is_local && !complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0); if (option_branch) { our_head_points_at = find_remote_branch(mapped_refs, option_branch); if (!our_head_points_at) die(_("Remote branch %s not found in upstream %s"), option_branch, option_origin); } else our_head_points_at = remote_head_points_at; } else { warning(_("You appear to have cloned an empty repository.")); mapped_refs = NULL; our_head_points_at = NULL; remote_head_points_at = NULL; remote_head = NULL; option_no_checkout = 1; if (!option_bare) install_branch_config(0, "master", option_origin, "refs/heads/master"); } write_refspec_config(src_ref_prefix, our_head_points_at, remote_head_points_at, &branch_top); if (is_local) clone_local(path, git_dir); else if (refs && complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); update_remote_refs(refs, mapped_refs, remote_head_points_at, branch_top.buf, reflog_msg.buf, transport); update_head(our_head_points_at, remote_head, reflog_msg.buf); transport_unlock_pack(transport); transport_disconnect(transport); junk_mode = JUNK_LEAVE_REPO; err = checkout(); strbuf_release(&reflog_msg); strbuf_release(&branch_top); strbuf_release(&key); strbuf_release(&value); junk_mode = JUNK_LEAVE_ALL; return err; }
int chk_positive(int result, char *msg) { if (result <= 0) die_errno("%s", msg); return result; }
static int cmd_import(const char *line) { int code; int dumpin_fd; char *note_msg; struct object_id head_oid; unsigned int startrev; struct child_process svndump_proc = CHILD_PROCESS_INIT; const char *command = "svnrdump"; if (read_ref(private_ref, &head_oid)) startrev = 0; else { note_msg = read_ref_note(&head_oid); if(note_msg == NULL) { warning("No note found for %s.", private_ref); startrev = 0; } else { struct rev_note note = { 0 }; if (parse_rev_note(note_msg, ¬e)) die("Revision number couldn't be parsed from note."); startrev = note.rev_nr + 1; free(note_msg); } } check_or_regenerate_marks(startrev - 1); if (dump_from_file) { dumpin_fd = open(url, O_RDONLY); if(dumpin_fd < 0) die_errno("Couldn't open svn dump file %s.", url); } else { svndump_proc.out = -1; argv_array_push(&svndump_proc.args, command); argv_array_push(&svndump_proc.args, "dump"); argv_array_push(&svndump_proc.args, url); argv_array_pushf(&svndump_proc.args, "-r%u:HEAD", startrev); code = start_command(&svndump_proc); if (code) die("Unable to start %s, code %d", command, code); dumpin_fd = svndump_proc.out; } /* setup marks file import/export */ printf("feature import-marks-if-exists=%s\n" "feature export-marks=%s\n", marksfilename, marksfilename); svndump_init_fd(dumpin_fd, STDIN_FILENO); svndump_read(url, private_ref, notes_ref); svndump_deinit(); svndump_reset(); close(dumpin_fd); if (!dump_from_file) { code = finish_command(&svndump_proc); if (code) warning("%s, returned %d", command, code); } return 0; }
int main(int argc, char *argv[]) { unsigned int login = 0, usage = 0; if (argc > 1) { if (!strcmp(argv[1], "-l")) { login = 1; argv[1] = argv[0]; argv++; argc--; } else if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) { printf("%s version %s\n Supported OSes: %s\n", argv[0], version, supported_oses); exit(0); } else if (*argv[1] == '-') { warn("unknown option: %s", argv[1]); usage = 2; } } if (argc < 2) usage = 1; if (usage) die(usage, "usage: %s [-l] <program> [args...]\n%s", argv[0], usage_msg); unsigned int os = 0; struct utsname u; if (uname(&u)) { warn_errno("uname failed"); goto reattach_failed; } if (strcmp(u.sysname, "Darwin")) { warn("unsupported OS sysname: %s", u.sysname); goto reattach_failed; } char *rest, *whole = strdup(u.release); if (!whole) { warn_errno("strdup failed"); goto reattach_failed; } rest = whole; strsep(&rest, "."); if (whole && *whole && whole != rest) { int major = atoi(whole); os = 100000; /* 10.1, 10.0 and prior betas/previews */ if (major >= 6) /* 10.2 and newer */ os += (major-4) * 100; } else warn("unparsable major release number: '%s'", u.release); free(whole); /* * change the 'os' variable to represent the "reattach variation" * instead of the major OS release * * older => 100500 with warning * 10.5 => 100500 * 10.6 => 100600 * 10.7 => 100600 * 10.8 => 100600 * 10.9 => 100600 * 10.10=> 101000 * 10.11=> 101000 * 10.12=> 101000 * newer => 101000 with warning */ if (100600 <= os && os <= 100900) os = 100600; else if (101000 <= os && os <= 101200) os = 101000; else if (os < 100500) { warn("%s: unsupported old OS, trying as if it were 10.5", argv[0]); os = 100500; } else if (os > 101200) { warn("%s: unsupported new OS, trying as if it were 10.10", argv[0]); os = 101000; } if (move_to_user_namespace(os) != 0) { reattach_failed: warn("%s: unable to reattach", argv[0]); } char **newargs = NULL; const char *file = argv[1]; if (login) { /* * For their argv[0], take the bit of file after the * last slash (the whole thing if there is no slash * or if that bit would be zero length) and prefix * it with '-'. */ char *arg0 = malloc(strlen(file) + 2); if (!arg0) goto exec_it; *arg0 = '-'; char *slash = strrchr(file, '/'); if (slash && slash[1]) strcpy(arg0+1, slash+1); else strcpy(arg0+1, file); /* use the rest of the args as they are */ newargs = malloc(sizeof(*newargs) * (argc)); if (!newargs) { free(arg0); goto exec_it; } newargs[0] = arg0; int arg = 2; for(; arg < argc; arg++) newargs[arg-1] = argv[arg]; newargs[arg-1] = NULL; } exec_it: if (execvp(file, newargs ? newargs : argv+1) < 0) die_errno(3, "%s: execv failed", argv[0]); if (newargs) { free(newargs[0]); free(newargs); } return 0; }
int main(int argc, char **argv) { char *prog; struct commands *cmd; int devnull_fd; /* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids not messing up when the pipes are * dup'ed onto stdin/stdout/stderr in the child processes we spawn. */ devnull_fd = open("/dev/null", O_RDWR); while (devnull_fd >= 0 && devnull_fd <= 2) devnull_fd = dup(devnull_fd); if (devnull_fd == -1) die_errno("opening /dev/null failed"); close (devnull_fd); /* * Special hack to pretend to be a CVS server */ if (argc == 2 && !strcmp(argv[1], "cvs server")) argv--; /* * We do not accept anything but "-c" followed by "cmd arg", * where "cmd" is a very limited subset of git commands. */ else if (argc != 3 || strcmp(argv[1], "-c")) { if (chdir(COMMAND_DIR)) die("Sorry, the interactive git-shell is not enabled"); for (;;) { prog = readline("git> "); if (prog == NULL) { printf("\n"); exit(0); } else if (!strcmp(prog, "quit") || !strcmp(prog, "logout") || !strcmp(prog, "exit")) { exit(0); } else if (!strcmp(prog, "")) { } else if (is_valid_cmd_name(prog)) { run(prog); add_history(prog); } else { printf("invalid command format '%s'\n", prog); } free(prog); }; } prog = argv[2]; if (!strncmp(prog, "git", 3) && isspace(prog[3])) /* Accept "git foo" as if the caller said "git-foo". */ prog[3] = '-'; for (cmd = cmd_list ; cmd->name ; cmd++) { int len = strlen(cmd->name); char *arg; if (strncmp(cmd->name, prog, len)) continue; arg = NULL; switch (prog[len]) { case '\0': arg = NULL; break; case ' ': arg = prog + len + 1; break; default: continue; } exit(cmd->exec(cmd->name, arg)); } if (chdir(COMMAND_DIR)) die("unrecognized command '%s'", prog); if (is_valid_cmd_name(prog)) execl(prog, prog, (char *) NULL); die("unrecognized command '%s'", prog); }
int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0, is_local; struct stat buf; const char *repo_name, *repo, *work_tree, *git_dir; char *path, *dir; int dest_exists; const struct ref *refs, *remote_head; const struct ref *remote_head_points_at; const struct ref *our_head_points_at; struct ref *mapped_refs; const struct ref *ref; struct strbuf key = STRBUF_INIT, value = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; const char *src_ref_prefix = "refs/heads/"; struct remote *remote; int err = 0, complete_refs_before_fetch = 1; int submodule_progress; struct refspec *refspec; const char *fetch_pattern; packet_trace_identity("clone"); argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); if (argc > 2) usage_msg_opt(_("Too many arguments."), builtin_clone_usage, builtin_clone_options); if (argc == 0) usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); if (option_depth || option_since || option_not.nr) deepen = 1; if (option_single_branch == -1) option_single_branch = deepen ? 1 : 0; if (option_mirror) option_bare = 1; if (option_bare) { if (option_origin) die(_("--bare and --origin %s options are incompatible."), option_origin); if (real_git_dir) die(_("--bare and --separate-git-dir are incompatible.")); option_no_checkout = 1; } if (!option_origin) option_origin = "origin"; repo_name = argv[0]; path = get_repo_path(repo_name, &is_bundle); if (path) repo = xstrdup(absolute_path(repo_name)); else if (!strchr(repo_name, ':')) die(_("repository '%s' does not exist"), repo_name); else repo = repo_name; /* no need to be strict, transport_set_option() will validate it again */ if (option_depth && atoi(option_depth) < 1) die(_("depth %s is not a positive number"), option_depth); if (argc == 2) dir = xstrdup(argv[1]); else dir = guess_dir_name(repo_name, is_bundle, option_bare); strip_trailing_slashes(dir); dest_exists = !stat(dir, &buf); if (dest_exists && !is_empty_dir(dir)) die(_("destination path '%s' already exists and is not " "an empty directory."), dir); strbuf_addf(&reflog_msg, "clone: from %s", repo); if (option_bare) work_tree = NULL; else { work_tree = getenv("GIT_WORK_TREE"); if (work_tree && !stat(work_tree, &buf)) die(_("working tree '%s' already exists."), work_tree); } if (option_bare || work_tree) git_dir = xstrdup(dir); else { work_tree = dir; git_dir = mkpathdup("%s/.git", dir); } atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); if (!option_bare) { if (safe_create_leading_directories_const(work_tree) < 0) die_errno(_("could not create leading directories of '%s'"), work_tree); if (!dest_exists && mkdir(work_tree, 0777)) die_errno(_("could not create work tree dir '%s'"), work_tree); junk_work_tree = work_tree; set_git_work_tree(work_tree); } junk_git_dir = real_git_dir ? real_git_dir : git_dir; if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); if (0 <= option_verbosity) { if (option_bare) fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir); else fprintf(stderr, _("Cloning into '%s'...\n"), dir); } if (option_recursive) { if (option_required_reference.nr && option_optional_reference.nr) die(_("clone --recursive is not compatible with " "both --reference and --reference-if-able")); else if (option_required_reference.nr) { string_list_append(&option_config, "submodule.alternateLocation=superproject"); string_list_append(&option_config, "submodule.alternateErrorStrategy=die"); } else if (option_optional_reference.nr) { string_list_append(&option_config, "submodule.alternateLocation=superproject"); string_list_append(&option_config, "submodule.alternateErrorStrategy=info"); } } init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET); if (real_git_dir) git_dir = real_git_dir; write_config(&option_config); git_config(git_default_config, NULL); if (option_bare) { if (option_mirror) src_ref_prefix = "refs/"; strbuf_addstr(&branch_top, src_ref_prefix); git_config_set("core.bare", "true"); } else { strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); } strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); strbuf_addf(&key, "remote.%s.url", option_origin); git_config_set(key.buf, repo); strbuf_reset(&key); if (option_required_reference.nr || option_optional_reference.nr) setup_reference(); fetch_pattern = value.buf; refspec = parse_fetch_refspec(1, &fetch_pattern); strbuf_reset(&value); remote = remote_get(option_origin); transport = transport_get(remote, remote->url[0]); transport_set_verbosity(transport, option_verbosity, option_progress); transport->family = family; path = get_repo_path(remote->url[0], &is_bundle); is_local = option_local != 0 && path && !is_bundle; if (is_local) { if (option_depth) warning(_("--depth is ignored in local clones; use file:// instead.")); if (option_since) warning(_("--shallow-since is ignored in local clones; use file:// instead.")); if (option_not.nr) warning(_("--shallow-exclude is ignored in local clones; use file:// instead.")); if (!access(mkpath("%s/shallow", path), F_OK)) { if (option_local > 0) warning(_("source repository is shallow, ignoring --local")); is_local = 0; } } if (option_local > 0 && !is_local) warning(_("--local is ignored")); transport->cloning = 1; if (!transport->get_refs_list || (!is_local && !transport->fetch)) die(_("Don't know how to clone %s"), transport->url); transport_set_option(transport, TRANS_OPT_KEEP, "yes"); if (option_depth) transport_set_option(transport, TRANS_OPT_DEPTH, option_depth); if (option_since) transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE, option_since); if (option_not.nr) transport_set_option(transport, TRANS_OPT_DEEPEN_NOT, (const char *)&option_not); if (option_single_branch) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); if (option_upload_pack) transport_set_option(transport, TRANS_OPT_UPLOADPACK, option_upload_pack); if (transport->smart_options && !deepen) transport->smart_options->check_self_contained_and_connected = 1; refs = transport_get_remote_refs(transport); if (refs) { mapped_refs = wanted_peer_refs(refs, refspec); /* * transport_get_remote_refs() may return refs with null sha-1 * in mapped_refs (see struct transport->get_refs_list * comment). In that case we need fetch it early because * remote_head code below relies on it. * * for normal clones, transport_get_remote_refs() should * return reliable ref set, we can delay cloning until after * remote HEAD check. */ for (ref = refs; ref; ref = ref->next) if (is_null_oid(&ref->old_oid)) { complete_refs_before_fetch = 0; break; } if (!is_local && !complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0); if (option_branch) { our_head_points_at = find_remote_branch(mapped_refs, option_branch); if (!our_head_points_at) die(_("Remote branch %s not found in upstream %s"), option_branch, option_origin); } else our_head_points_at = remote_head_points_at; } else { if (option_branch) die(_("Remote branch %s not found in upstream %s"), option_branch, option_origin); warning(_("You appear to have cloned an empty repository.")); mapped_refs = NULL; our_head_points_at = NULL; remote_head_points_at = NULL; remote_head = NULL; option_no_checkout = 1; if (!option_bare) install_branch_config(0, "master", option_origin, "refs/heads/master"); } write_refspec_config(src_ref_prefix, our_head_points_at, remote_head_points_at, &branch_top); if (is_local) clone_local(path, git_dir); else if (refs && complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); update_remote_refs(refs, mapped_refs, remote_head_points_at, branch_top.buf, reflog_msg.buf, transport, !is_local); update_head(our_head_points_at, remote_head, reflog_msg.buf); /* * We want to show progress for recursive submodule clones iff * we did so for the main clone. But only the transport knows * the final decision for this flag, so we need to rescue the value * before we free the transport. */ submodule_progress = transport->progress; transport_unlock_pack(transport); transport_disconnect(transport); if (option_dissociate) { close_all_packs(); dissociate_from_references(); } junk_mode = JUNK_LEAVE_REPO; err = checkout(submodule_progress); strbuf_release(&reflog_msg); strbuf_release(&branch_top); strbuf_release(&key); strbuf_release(&value); junk_mode = JUNK_LEAVE_ALL; free(refspec); return err; }
int start_command(struct child_process *cmd) { int need_in, need_out, need_err; int fdin[2], fdout[2], fderr[2]; int failed_errno = failed_errno; /* * In case of errors we must keep the promise to close FDs * that have been passed in via ->in and ->out. */ need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { if (pipe(fdin) < 0) { failed_errno = errno; if (cmd->out > 0) close(cmd->out); goto fail_pipe; } cmd->in = fdin[1]; } need_out = !cmd->no_stdout && !cmd->stdout_to_stderr && cmd->out < 0; if (need_out) { if (pipe(fdout) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); goto fail_pipe; } cmd->out = fdout[0]; } need_err = !cmd->no_stderr && cmd->err < 0; if (need_err) { if (pipe(fderr) < 0) { failed_errno = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); fail_pipe: error("cannot create pipe for %s: %s", cmd->argv[0], strerror(failed_errno)); errno = failed_errno; return -1; } cmd->err = fderr[0]; } trace_argv_printf(cmd->argv, "trace: run_command:"); #ifndef WIN32 { int notify_pipe[2]; if (pipe(notify_pipe)) notify_pipe[0] = notify_pipe[1] = -1; fflush(NULL); cmd->pid = fork(); if (!cmd->pid) { /* * Redirect the channel to write syscall error messages to * before redirecting the process's stderr so that all die() * in subsequent call paths use the parent's stderr. */ if (cmd->no_stderr || need_err) { child_err = dup(2); set_cloexec(child_err); } set_die_routine(die_child); close(notify_pipe[0]); set_cloexec(notify_pipe[1]); child_notifier = notify_pipe[1]; atexit(notify_parent); if (cmd->no_stdin) dup_devnull(0); else if (need_in) { dup2(fdin[0], 0); close_pair(fdin); } else if (cmd->in) { dup2(cmd->in, 0); close(cmd->in); } if (cmd->no_stderr) dup_devnull(2); else if (need_err) { dup2(fderr[1], 2); close_pair(fderr); } else if (cmd->err > 1) { dup2(cmd->err, 2); close(cmd->err); } if (cmd->no_stdout) dup_devnull(1); else if (cmd->stdout_to_stderr) dup2(2, 1); else if (need_out) { dup2(fdout[1], 1); close_pair(fdout); } else if (cmd->out > 1) { dup2(cmd->out, 1); close(cmd->out); } if (cmd->dir && chdir(cmd->dir)) die_errno("exec '%s': cd to '%s' failed", cmd->argv[0], cmd->dir); if (cmd->env) { for (; *cmd->env; cmd->env++) { if (strchr(*cmd->env, '=')) putenv((char *)*cmd->env); else unsetenv(*cmd->env); } } if (cmd->preexec_cb) { /* * We cannot predict what the pre-exec callback does. * Forgo parent notification. */ close(child_notifier); child_notifier = -1; cmd->preexec_cb(); } if (cmd->git_cmd) { execv_git_cmd(cmd->argv); } else if (cmd->use_shell) { execv_shell_cmd(cmd->argv); } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } /* * Do not check for cmd->silent_exec_failure; the parent * process will check it when it sees this exit code. */ if (errno == ENOENT) exit(127); else die_errno("cannot exec '%s'", cmd->argv[0]); } if (cmd->pid < 0) error("cannot fork() for %s: %s", cmd->argv[0], strerror(failed_errno = errno)); /* * Wait for child's execvp. If the execvp succeeds (or if fork() * failed), EOF is seen immediately by the parent. Otherwise, the * child process sends a single byte. * Note that use of this infrastructure is completely advisory, * therefore, we keep error checks minimal. */ close(notify_pipe[1]); if (read(notify_pipe[0], ¬ify_pipe[1], 1) == 1) { /* * At this point we know that fork() succeeded, but execvp() * failed. Errors have been reported to our stderr. */ wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure); failed_errno = errno; cmd->pid = -1; } close(notify_pipe[0]); } #else { int fhin = 0, fhout = 1, fherr = 2; const char **sargv = cmd->argv; char **env = environ; if (cmd->no_stdin) fhin = open("/dev/null", O_RDWR); else if (need_in) fhin = dup(fdin[0]); else if (cmd->in) fhin = dup(cmd->in); if (cmd->no_stderr) fherr = open("/dev/null", O_RDWR); else if (need_err) fherr = dup(fderr[1]); else if (cmd->err > 2) fherr = dup(cmd->err); if (cmd->no_stdout) fhout = open("/dev/null", O_RDWR); else if (cmd->stdout_to_stderr) fhout = dup(fherr); else if (need_out) fhout = dup(fdout[1]); else if (cmd->out > 1) fhout = dup(cmd->out); if (cmd->env) env = make_augmented_environ(cmd->env); if (cmd->git_cmd) { cmd->argv = prepare_git_cmd(cmd->argv); } else if (cmd->use_shell) { cmd->argv = prepare_shell_cmd(cmd->argv); } cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env, cmd->dir, fhin, fhout, fherr); failed_errno = errno; if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT)) error("cannot spawn %s: %s", cmd->argv[0], strerror(errno)); if (cmd->env) free_environ(env); if (cmd->git_cmd) free(cmd->argv); cmd->argv = sargv; if (fhin != 0) close(fhin); if (fhout != 1) close(fhout); if (fherr != 2) close(fherr); } #endif if (cmd->pid < 0) { if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); if (need_err) close_pair(fderr); else if (cmd->err) close(cmd->err); errno = failed_errno; return -1; } if (need_in) close(fdin[0]); else if (cmd->in) close(cmd->in); if (need_out) close(fdout[1]); else if (cmd->out) close(cmd->out); if (need_err) close(fderr[1]); else if (cmd->err) close(cmd->err); return 0; }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, newfd, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out. */ dest_path = internal_copy_pathspec(prefix, argv + argc, 1, KEEP_TRAILING_SLASH); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die("destination '%s' is not a directory", dest_path[0]); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { int first = cache_name_pos(src, length); if (first >= 0) { struct strbuf submodule_dotgit = STRBUF_INIT; if (!S_ISGITLINK(active_cache[first]->ce_mode)) die (_("Huh? Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok()) die (_("Please, stage your changes to .gitmodules or stash them to proceed")); strbuf_addf(&submodule_dotgit, "%s/.git", src); submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); if (submodule_gitfile[i]) submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); else submodule_gitfile[i] = SUBMODULE_WITH_GITDIR; strbuf_release(&submodule_dotgit); } else { const char *src_w_slash = add_slash(src); int last, len_w_slash = length + 1; modes[i] = WORKING_DIRECTORY; first = cache_name_pos(src_w_slash, len_w_slash); if (first >= 0) die (_("Huh? %.*s is in index?"), len_w_slash, src_w_slash); first = -1 - first; for (last = first; last < active_nr; last++) { const char *path = active_cache[last]->name; if (strncmp(path, src_w_slash, len_w_slash)) break; } free((char *)src_w_slash); if (last - first < 1) bad = _("source directory is empty"); else { int j, dst_len; if (last - first > 0) { source = xrealloc(source, (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, (argc + last - first) * sizeof(enum update_mode)); submodule_gitfile = xrealloc(submodule_gitfile, (argc + last - first) * sizeof(char *)); } dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; submodule_gitfile[argc + j] = NULL; } argc += last - first; } } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); else if (lstat(dst, &st) == 0) { bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { if (verbose) warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) bad = _("multiple sources for the same target"); else if (is_dir_sep(dst[strlen(dst) - 1])) bad = _("destination directory does not exist"); else string_list_insert(&src_for_dst, dst); if (bad) { if (ignore_errors) { if (--argc > 0) { memmove(source + i, source + i + 1, (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, (argc - i) * sizeof(char *)); memmove(modes + i, modes + i + 1, (argc - i) * sizeof(enum update_mode)); memmove(submodule_gitfile + i, submodule_gitfile + i + 1, (argc - i) * sizeof(char *)); i--; } } else die (_("%s, source=%s, destination=%s"), bad, src, dst); } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (!show_only && mode != INDEX) { if (rename(src, dst) < 0 && !ignore_errors) die_errno (_("renaming '%s' failed"), src); if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; } } if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (gitmodules_modified) stage_updated_gitmodules(); if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return 0; }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, flags, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); if (read_cache() < 0) die(_("index file corrupt")); source = internal_prefix_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out, except in the case * "git mv directory no-such-dir/". */ flags = KEEP_TRAILING_SLASH; if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) flags = 0; dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die(_("destination '%s' is not a directory"), dest_path[0]); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { int first = cache_name_pos(src, length), last; if (first >= 0) prepare_move_submodule(src, first, submodule_gitfile + i); else if (index_range_of_same_dir(src, length, &first, &last) < 1) bad = _("source directory is empty"); else { /* last - first >= 1 */ int j, dst_len, n; modes[i] = WORKING_DIRECTORY; n = argc + last - first; REALLOC_ARRAY(source, n); REALLOC_ARRAY(destination, n); REALLOC_ARRAY(modes, n); REALLOC_ARRAY(submodule_gitfile, n); dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; submodule_gitfile[argc + j] = NULL; } argc += last - first; } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); else if (lstat(dst, &st) == 0 && (!ignore_case || strcasecmp(src, dst))) { bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { if (verbose) warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) bad = _("multiple sources for the same target"); else if (is_dir_sep(dst[strlen(dst) - 1])) bad = _("destination directory does not exist"); else string_list_insert(&src_for_dst, dst); if (!bad) continue; if (!ignore_errors) die(_("%s, source=%s, destination=%s"), bad, src, dst); if (--argc > 0) { int n = argc - i; memmove(source + i, source + i + 1, n * sizeof(char *)); memmove(destination + i, destination + i + 1, n * sizeof(char *)); memmove(modes + i, modes + i + 1, n * sizeof(enum update_mode)); memmove(submodule_gitfile + i, submodule_gitfile + i + 1, n * sizeof(char *)); i--; } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (show_only) continue; if (mode != INDEX && rename(src, dst) < 0) { if (ignore_errors) continue; die_errno(_("renaming '%s' failed"), src); } if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; } if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (gitmodules_modified) stage_updated_gitmodules(); if (active_cache_changed && write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); return 0; }
static int handle_alias(int *argcp, const char ***argv) { int envchanged = 0, ret = 0, saved_errno = errno; const char *subdir; int count, option_count; const char **new_argv; const char *alias_command; char *alias_string; int unused_nongit; subdir = setup_git_directory_gently(&unused_nongit); alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { if (alias_string[0] == '!') { commit_pager_choice(); if (*argcp > 1) { struct strbuf buf; strbuf_init(&buf, PATH_MAX); strbuf_addstr(&buf, alias_string); sq_quote_argv(&buf, (*argv) + 1, PATH_MAX); free(alias_string); alias_string = buf.buf; } trace_printf("trace: alias to shell cmd: %s => %s\n", alias_command, alias_string + 1); ret = system(alias_string + 1); if (ret >= 0 && WIFEXITED(ret) && WEXITSTATUS(ret) != 127) exit(WEXITSTATUS(ret)); die("Failed to run '%s' when expanding alias '%s'", alias_string + 1, alias_command); } count = split_cmdline(alias_string, &new_argv); if (count < 0) die("Bad alias.%s string: %s", alias_command, split_cmdline_strerror(count)); option_count = handle_options(&new_argv, &count, &envchanged); if (envchanged) die("alias '%s' changes environment variables\n" "You can use '!git' in the alias to do this.", alias_command); memmove(new_argv - option_count, new_argv, count * sizeof(char *)); new_argv -= option_count; if (count < 1) die("empty alias for %s", alias_command); if (!strcmp(alias_command, new_argv[0])) die("recursive alias: %s", alias_command); trace_argv_printf(new_argv, "trace: alias expansion: %s =>", alias_command); new_argv = xrealloc(new_argv, sizeof(char *) * (count + *argcp)); /* insert after command name */ memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp); *argv = new_argv; *argcp += count - 1; ret = 1; } if (subdir && chdir(subdir)) die_errno("Cannot change to '%s'", subdir); errno = saved_errno; return ret; }
static int handle_options(const char ***argv, int *argc, int *envchanged) { const char **orig_argv = *argv; while (*argc > 0) { const char *cmd = (*argv)[0]; if (cmd[0] != '-') break; /* * For legacy reasons, the "version" and "help" * commands can be written with "--" prepended * to make them look like flags. */ if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version")) break; /* * Check remaining flags. */ if (skip_prefix(cmd, "--exec-path", &cmd)) { if (*cmd == '=') git_set_exec_path(cmd + 1); else { puts(git_exec_path()); exit(0); } } else if (!strcmp(cmd, "--html-path")) { puts(system_path(GIT_HTML_PATH)); exit(0); } else if (!strcmp(cmd, "--man-path")) { puts(system_path(GIT_MAN_PATH)); exit(0); } else if (!strcmp(cmd, "--info-path")) { puts(system_path(GIT_INFO_PATH)); exit(0); } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) { use_pager = 1; } else if (!strcmp(cmd, "-P") || !strcmp(cmd, "--no-pager")) { use_pager = 0; if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--no-replace-objects")) { check_replace_refs = 0; setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--git-dir")) { if (*argc < 2) { fprintf(stderr, _("no directory given for --git-dir\n" )); usage(git_usage_string); } setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (skip_prefix(cmd, "--git-dir=", &cmd)) { setenv(GIT_DIR_ENVIRONMENT, cmd, 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--namespace")) { if (*argc < 2) { fprintf(stderr, _("no namespace given for --namespace\n" )); usage(git_usage_string); } setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (skip_prefix(cmd, "--namespace=", &cmd)) { setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--work-tree")) { if (*argc < 2) { fprintf(stderr, _("no directory given for --work-tree\n" )); usage(git_usage_string); } setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (skip_prefix(cmd, "--work-tree=", &cmd)) { setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--super-prefix")) { if (*argc < 2) { fprintf(stderr, _("no prefix given for --super-prefix\n" )); usage(git_usage_string); } setenv(GIT_SUPER_PREFIX_ENVIRONMENT, (*argv)[1], 1); if (envchanged) *envchanged = 1; (*argv)++; (*argc)--; } else if (skip_prefix(cmd, "--super-prefix=", &cmd)) { setenv(GIT_SUPER_PREFIX_ENVIRONMENT, cmd, 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--bare")) { char *cwd = xgetcwd(); is_bare_repository_cfg = 1; setenv(GIT_DIR_ENVIRONMENT, cwd, 0); free(cwd); setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "-c")) { if (*argc < 2) { fprintf(stderr, _("-c expects a configuration string\n" )); usage(git_usage_string); } git_config_push_parameter((*argv)[1]); (*argv)++; (*argc)--; } else if (!strcmp(cmd, "--literal-pathspecs")) { setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--no-literal-pathspecs")) { setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--glob-pathspecs")) { setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--noglob-pathspecs")) { setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--icase-pathspecs")) { setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--no-optional-locks")) { setenv(GIT_OPTIONAL_LOCKS_ENVIRONMENT, "0", 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--shallow-file")) { (*argv)++; (*argc)--; set_alternate_shallow_file((*argv)[0], 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "-C")) { if (*argc < 2) { fprintf(stderr, _("no directory given for -C\n" )); usage(git_usage_string); } if ((*argv)[1][0]) { if (chdir((*argv)[1])) die_errno("cannot change to '%s'", (*argv)[1]); if (envchanged) *envchanged = 1; } (*argv)++; (*argc)--; } else if (!strcmp(cmd, "--list-builtins")) { list_builtins(0, '\n'); exit(0); } else if (!strcmp(cmd, "--list-parseopt-builtins")) { list_builtins(NO_PARSEOPT, ' '); exit(0); } else { fprintf(stderr, _("unknown option: %s\n"), cmd); usage(git_usage_string); } (*argv)++; (*argc)--; } return (*argv) - orig_argv; }
static int handle_alias(int *argcp, const char ***argv) { int envchanged = 0, ret = 0, saved_errno = errno; int count, option_count; const char **new_argv; const char *alias_command; char *alias_string; alias_command = (*argv)[0]; alias_string = alias_lookup(alias_command); if (alias_string) { if (alias_string[0] == '!') { struct child_process child = CHILD_PROCESS_INIT; int nongit_ok; /* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */ setup_git_directory_gently(&nongit_ok); commit_pager_choice(); child.use_shell = 1; argv_array_push(&child.args, alias_string + 1); argv_array_pushv(&child.args, (*argv) + 1); ret = run_command(&child); if (ret >= 0) /* normal exit */ exit(ret); die_errno("while expanding alias '%s': '%s'", alias_command, alias_string + 1); } count = split_cmdline(alias_string, &new_argv); if (count < 0) die("Bad alias.%s string: %s", alias_command, split_cmdline_strerror(count)); option_count = handle_options(&new_argv, &count, &envchanged); if (envchanged) die("alias '%s' changes environment variables.\n" "You can use '!git' in the alias to do this", alias_command); memmove(new_argv - option_count, new_argv, count * sizeof(char *)); new_argv -= option_count; if (count < 1) die("empty alias for %s", alias_command); if (!strcmp(alias_command, new_argv[0])) die("recursive alias: %s", alias_command); trace_argv_printf(new_argv, "trace: alias expansion: %s =>", alias_command); REALLOC_ARRAY(new_argv, count + *argcp); /* insert after command name */ memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp); *argv = new_argv; *argcp += count - 1; ret = 1; } errno = saved_errno; return ret; }
int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; struct setup_revision_opt s_r_opt; int nr = 0, total, i; int use_stdout = 0; int start_number = -1; int numbered_files = 0; /* _just_ numbers */ int ignore_if_in_upstream = 0; int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; int quiet = 0; char *branch_name = NULL; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, N_("use [PATCH n/m] even with a single patch"), PARSE_OPT_NOARG, numbered_callback }, { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, N_("use [PATCH] even with multiple patches"), PARSE_OPT_NOARG, no_numbered_callback }, OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")), OPT_BOOLEAN(0, "stdout", &use_stdout, N_("print patches to standard out")), OPT_BOOLEAN(0, "cover-letter", &cover_letter, N_("generate a cover letter")), OPT_BOOLEAN(0, "numbered-files", &numbered_files, N_("use simple number sequence for output file names")), OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), N_("use <sfx> instead of '.patch'")), OPT_INTEGER(0, "start-number", &start_number, N_("start numbering patches at <n> instead of 1")), { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"), N_("Use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback }, { OPTION_CALLBACK, 'o', "output-directory", &output_directory, N_("dir"), N_("store resulting files in <dir>"), PARSE_OPT_NONEG, output_directory_callback }, { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, N_("don't strip/add [PATCH]"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, OPT_BOOLEAN(0, "no-binary", &no_binary_diff, N_("don't output binary diffs")), OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, N_("don't include a patch matching a commit upstream")), { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL, N_("show patch format instead of default (patch + stat)"), PARSE_OPT_NONEG | PARSE_OPT_NOARG }, OPT_GROUP(N_("Messaging")), { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"), N_("add email header"), 0, header_callback }, { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"), 0, to_callback }, { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"), 0, cc_callback }, OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"), N_("make first mail a reply to <message-id>")), { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"), N_("attach the patch"), PARSE_OPT_OPTARG, attach_callback }, { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"), N_("inline the patch"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback }, { OPTION_CALLBACK, 0, "thread", &thread, N_("style"), N_("enable message threading, styles: shallow, deep"), PARSE_OPT_OPTARG, thread_callback }, OPT_STRING(0, "signature", &signature, N_("signature"), N_("add a signature")), OPT_BOOLEAN(0, "quiet", &quiet, N_("don't print the patch filenames")), OPT_END() }; extra_hdr.strdup_strings = 1; extra_to.strdup_strings = 1; extra_cc.strdup_strings = 1; init_grep_defaults(); git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; rev.max_parents = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; if (default_attach) { rev.mime_boundary = default_attach; rev.no_inline = 1; } /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ argc = parse_options(argc, argv, prefix, builtin_format_patch_options, builtin_format_patch_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); if (do_signoff) { const char *committer; const char *endpos; committer = git_committer_info(IDENT_STRICT); endpos = strchr(committer, '>'); if (!endpos) die(_("bogus committer info %s"), committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } for (i = 0; i < extra_hdr.nr; i++) { strbuf_addstr(&buf, extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); } if (extra_to.nr) strbuf_addstr(&buf, "To: "); for (i = 0; i < extra_to.nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_to.items[i].string); if (i + 1 < extra_to.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } if (extra_cc.nr) strbuf_addstr(&buf, "Cc: "); for (i = 0; i < extra_cc.nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_cc.items[i].string); if (i + 1 < extra_cc.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = strbuf_detach(&buf, NULL); if (start_number < 0) start_number = 1; /* * If numbered is set solely due to format.numbered in config, * and it would conflict with --keep-subject (-k) from the * command line, reset "numbered". */ if (numbered && keep_subject && !numbered_cmdline_opt) numbered = 0; if (numbered && keep_subject) die (_("-n and -k are mutually exclusive.")); if (keep_subject && subject_prefix) die (_("--subject-prefix and -k are mutually exclusive.")); rev.preserve_subject = keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); if (argc > 1) die (_("unrecognized argument: %s"), argv[1]); if (rev.diffopt.output_format & DIFF_FORMAT_NAME) die(_("--name-only does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS) die(_("--name-status does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF) die(_("--check does not make sense")); if (!use_patch_format && (!rev.diffopt.output_format || rev.diffopt.output_format == DIFF_FORMAT_PATCH)) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY; /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (rev.show_notes) init_display_notes(&rev.notes_opt); if (!use_stdout) output_directory = set_outdir(prefix, output_directory); else setup_pager(); if (output_directory) { if (use_stdout) die(_("standard output, or directory, which one?")); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) die_errno(_("Could not create directory '%s'"), output_directory); } if (rev.pending.nr == 1) { if (rev.max_count < 0 && !rev.show_root_diff) { /* * This is traditional behaviour of "git format-patch * origin" that prepares what the origin side still * does not have. */ unsigned char sha1[20]; const char *ref; rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL); if (ref && !prefixcmp(ref, "refs/heads/")) branch_name = xstrdup(ref + strlen("refs/heads/")); else branch_name = xstrdup(""); /* no branch */ } /* * Otherwise, it is "format-patch -22 HEAD", and/or * "format-patch --root HEAD". The user wants * get_revision() to do the usual traversal. */ } /* * We cannot move this anywhere earlier because we do want to * know if --root was given explicitly from the command line. */ rev.show_root_diff = 1; if (cover_letter) { /* * NEEDSWORK:randomly pick one positive commit to show * diffstat; this is often the tip and the command * happens to do the right thing in most cases, but a * complex command like "--cover-letter a b c ^bottom" * picks "c" and shows diffstat between bottom..c * which may not match what the series represents at * all and totally broken. */ int i; for (i = 0; i < rev.pending.nr; i++) { struct object *o = rev.pending.objects[i].item; if (!(o->flags & UNINTERESTING)) head = (struct commit *)o; } /* There is nothing to show; it is not an error, though. */ if (!head) return 0; if (!branch_name) branch_name = find_branch_name(&rev); } if (ignore_if_in_upstream) { /* Don't say anything if head and upstream are the same. */ if (rev.pending.nr == 2) { struct object_array_entry *o = rev.pending.objects; if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } get_patch_ids(&rev, &ids); } if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); if (prepare_revision_walk(&rev)) die(_("revision walk setup failed")); rev.boundary = 1; while ((commit = get_revision(&rev)) != NULL) { if (commit->object.flags & BOUNDARY) { boundary_count++; origin = (boundary_count == 1) ? commit : NULL; continue; } if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) continue; nr++; list = xrealloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; } total = nr; if (!keep_subject && auto_number && total > 1) numbered = 1; if (numbered) rev.total = total + start_number - 1; if (in_reply_to || thread || cover_letter) rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); if (in_reply_to) { const char *msgid = clean_message_id(in_reply_to); string_list_append(rev.ref_message_ids, msgid); } rev.numbered_files = numbered_files; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, use_stdout, numbered, numbered_files, origin, nr, list, head, branch_name, quiet); total++; start_number--; } rev.add_signoff = add_signoff; while (0 <= --nr) { int shown; commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ if (thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* * For deep threading: make every mail * a reply to the previous one, no * matter what other options are set. * * For shallow threading: * * Without --cover-letter and * --in-reply-to, make every mail a * reply to the one before. * * With --in-reply-to but no * --cover-letter, make every mail a * reply to the <reply-to>. * * With --cover-letter, make every * mail but the cover letter a reply * to the cover letter. The cover * letter is a reply to the * --in-reply-to, if specified. */ if (thread == THREAD_SHALLOW && rev.ref_message_ids->nr > 0 && (!cover_letter || rev.nr > 1)) free(rev.message_id); else string_list_append(rev.ref_message_ids, rev.message_id); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet)) die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; /* We put one extra blank line between formatted * patches and this flag is used by log-tree code * to see if it needs to emit a LF before showing * the log; when using one file per patch, we do * not want the extra blank line. */ if (!use_stdout) rev.shown_one = 0; if (shown) { if (rev.mime_boundary) printf("\n--%s%s--\n\n\n", mime_boundary_leader, rev.mime_boundary); else print_signature(); } if (!use_stdout) fclose(stdout); } free(list); free(branch_name); string_list_clear(&extra_to, 0); string_list_clear(&extra_cc, 0); string_list_clear(&extra_hdr, 0); if (ignore_if_in_upstream) free_patch_ids(&ids); return 0; }
static void create_note(const unsigned char *object, struct msg_arg *msg, int append_only, const unsigned char *prev, unsigned char *result) { char *path = NULL; if (msg->use_editor || !msg->given) { int fd; /* write the template message before editing: */ path = git_pathdup("NOTES_EDITMSG"); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd < 0) die_errno("could not create file '%s'", path); if (msg->given) write_or_die(fd, msg->buf.buf, msg->buf.len); else if (prev && !append_only) write_note_data(fd, prev); write_or_die(fd, note_template, strlen(note_template)); write_commented_object(fd, object); close(fd); strbuf_reset(&(msg->buf)); if (launch_editor(path, &(msg->buf), NULL)) { die("Please supply the note contents using either -m" \ " or -F option"); } stripspace(&(msg->buf), 1); } if (prev && append_only) { /* Append buf to previous note contents */ unsigned long size; enum object_type type; char *prev_buf = read_sha1_file(prev, &type, &size); strbuf_grow(&(msg->buf), size + 1); if (msg->buf.len && prev_buf && size) strbuf_insert(&(msg->buf), 0, "\n", 1); if (prev_buf && size) strbuf_insert(&(msg->buf), 0, prev_buf, size); free(prev_buf); } if (!msg->buf.len) { fprintf(stderr, "Removing note for object %s\n", sha1_to_hex(object)); hashclr(result); } else { if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) { error("unable to write note object"); if (path) error("The note contents has been left in %s", path); exit(128); } } if (path) { unlink_or_warn(path); free(path); } }
void create_branch(const char *head, const char *name, const char *start_name, int force, int reflog, int clobber_head, int quiet, enum branch_track track) { struct ref_lock *lock = NULL; struct commit *commit; unsigned char sha1[20]; char *real_ref, msg[PATH_MAX + 20]; struct strbuf ref = STRBUF_INIT; int forcing = 0; int dont_change_ref = 0; int explicit_tracking = 0; if (track == BRANCH_TRACK_EXPLICIT || track == BRANCH_TRACK_OVERRIDE) explicit_tracking = 1; if (validate_new_branchname(name, &ref, force, track == BRANCH_TRACK_OVERRIDE || clobber_head)) { if (!force) dont_change_ref = 1; else forcing = 1; } real_ref = NULL; if (get_sha1(start_name, sha1)) { if (explicit_tracking) { if (advice_set_upstream_failure) { error(_(upstream_missing), start_name); advise(_(upstream_advice)); exit(1); } die(_(upstream_missing), start_name); } die(_("Not a valid object name: '%s'."), start_name); } switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) { case 0: /* Not branching from any existing branch */ if (explicit_tracking) die(_(upstream_not_branch), start_name); break; case 1: /* Unique completion -- good, only if it is a real branch */ if (!starts_with(real_ref, "refs/heads/") && validate_remote_tracking_branch(real_ref)) { if (explicit_tracking) die(_(upstream_not_branch), start_name); else real_ref = NULL; } break; default: die(_("Ambiguous object name: '%s'."), start_name); break; } if ((commit = lookup_commit_reference(sha1)) == NULL) die(_("Not a valid branch point: '%s'."), start_name); hashcpy(sha1, commit->object.sha1); if (!dont_change_ref) { lock = lock_any_ref_for_update(ref.buf, NULL, 0, NULL); if (!lock) die_errno(_("Failed to lock ref for update")); } if (reflog) log_all_ref_updates = 1; if (forcing) snprintf(msg, sizeof msg, "branch: Reset to %s", start_name); else if (!dont_change_ref) snprintf(msg, sizeof msg, "branch: Created from %s", start_name); if (real_ref && track) setup_tracking(ref.buf + 11, real_ref, track, quiet); if (!dont_change_ref) if (write_ref_sha1(lock, sha1, msg) < 0) die_errno(_("Failed to write ref")); strbuf_release(&ref); free(real_ref); }
/* * Make a pack stream and spit it out into file descriptor fd */ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *extra, struct send_pack_args *args) { /* * The child becomes pack-objects --revs; we feed * the revision parameters to it via its stdin and * let its stdout go back to the other end. */ const char *argv[] = { "pack-objects", "--all-progress-implied", "--revs", "--stdout", NULL, NULL, NULL, NULL, NULL, }; struct child_process po; int i; i = 4; if (args->use_thin_pack) argv[i++] = "--thin"; if (args->use_ofs_delta) argv[i++] = "--delta-base-offset"; if (args->quiet || !args->progress) argv[i++] = "-q"; if (args->progress) argv[i++] = "--progress"; memset(&po, 0, sizeof(po)); po.argv = argv; po.in = -1; po.out = args->stateless_rpc ? -1 : fd; po.git_cmd = 1; if (start_command(&po)) die_errno("git pack-objects failed"); /* * We feed the pack-objects we just spawned with revision * parameters by writing to the pipe. */ for (i = 0; i < extra->nr; i++) if (!feed_object(extra->array[i], po.in, 1)) break; while (refs) { if (!is_null_sha1(refs->old_sha1) && !feed_object(refs->old_sha1, po.in, 1)) break; if (!is_null_sha1(refs->new_sha1) && !feed_object(refs->new_sha1, po.in, 0)) break; refs = refs->next; } close(po.in); if (args->stateless_rpc) { char *buf = xmalloc(LARGE_PACKET_MAX); while (1) { ssize_t n = xread(po.out, buf, LARGE_PACKET_MAX); if (n <= 0) break; send_sideband(fd, -1, buf, n, LARGE_PACKET_MAX); } free(buf); close(po.out); po.out = -1; } if (finish_command(&po)) return -1; return 0; }
int cmd_stash(int argc, const char **argv, const char *prefix) { int i = -1; pid_t pid = getpid(); const char *index_file; struct argv_array args = ARGV_ARRAY_INIT; struct option options[] = { OPT_END() }; if (!use_builtin_stash()) { const char *path = mkpath("%s/git-legacy-stash", git_exec_path()); if (sane_execvp(path, (char **)argv) < 0) die_errno(_("could not exec %s"), path); else BUG("sane_execvp() returned???"); } prefix = setup_git_directory(); trace_repo_setup(prefix); setup_work_tree(); git_config(git_diff_basic_config, NULL); argc = parse_options(argc, argv, prefix, options, git_stash_usage, PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); index_file = get_index_file(); strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file, (uintmax_t)pid); if (!argc) return !!push_stash(0, NULL, prefix); else if (!strcmp(argv[0], "apply")) return !!apply_stash(argc, argv, prefix); else if (!strcmp(argv[0], "clear")) return !!clear_stash(argc, argv, prefix); else if (!strcmp(argv[0], "drop")) return !!drop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "pop")) return !!pop_stash(argc, argv, prefix); else if (!strcmp(argv[0], "branch")) return !!branch_stash(argc, argv, prefix); else if (!strcmp(argv[0], "list")) return !!list_stash(argc, argv, prefix); else if (!strcmp(argv[0], "show")) return !!show_stash(argc, argv, prefix); else if (!strcmp(argv[0], "store")) return !!store_stash(argc, argv, prefix); else if (!strcmp(argv[0], "create")) return !!create_stash(argc, argv, prefix); else if (!strcmp(argv[0], "push")) return !!push_stash(argc, argv, prefix); else if (!strcmp(argv[0], "save")) return !!save_stash(argc, argv, prefix); else if (*argv[0] != '-') usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), git_stash_usage, options); if (strcmp(argv[0], "-p")) { while (++i < argc && strcmp(argv[i], "--")) { /* * `akpqu` is a string which contains all short options, * except `-m` which is verified separately. */ if ((strlen(argv[i]) == 2) && *argv[i] == '-' && strchr("akpqu", argv[i][1])) continue; if (!strcmp(argv[i], "--all") || !strcmp(argv[i], "--keep-index") || !strcmp(argv[i], "--no-keep-index") || !strcmp(argv[i], "--patch") || !strcmp(argv[i], "--quiet") || !strcmp(argv[i], "--include-untracked")) continue; /* * `-m` and `--message=` are verified separately because * they need to be immediately followed by a string * (i.e.`-m"foobar"` or `--message="foobar"`). */ if (starts_with(argv[i], "-m") || starts_with(argv[i], "--message=")) continue; usage_with_options(git_stash_usage, options); } } argv_array_push(&args, "push"); argv_array_pushv(&args, argv); return !!push_stash(args.argc, args.argv, prefix); }
/* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. */ const char *setup_git_directory_gently(int *nongit_ok) { const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); static char cwd[PATH_MAX+1]; const char *gitdirenv; const char *gitfile_dir; int len, offset, ceil_offset; /* * Let's assume that we are in a git repository. * If it turns out later that we are somewhere else, the value will be * updated accordingly. */ if (nongit_ok) *nongit_ok = 0; /* * If GIT_DIR is set explicitly, we're not going * to do any discovery, but we still do repository * validation. */ gitdirenv = getenv(GIT_DIR_ENVIRONMENT); if (gitdirenv) { if (PATH_MAX - 40 < strlen(gitdirenv)) die("'$%s' too big", GIT_DIR_ENVIRONMENT); if (is_git_directory(gitdirenv)) { static char buffer[1024 + 1]; const char *retval; if (!work_tree_env) { retval = set_work_tree(gitdirenv); /* config may override worktree */ if (check_repository_format_gently(nongit_ok)) return NULL; return retval; } if (check_repository_format_gently(nongit_ok)) return NULL; retval = get_relative_cwd(buffer, sizeof(buffer) - 1, get_git_work_tree()); if (!retval || !*retval) return NULL; set_git_dir(make_absolute_path(gitdirenv)); if (chdir(work_tree_env) < 0) die_errno ("Could not chdir to '%s'", work_tree_env); strcat(buffer, "/"); return retval; } if (nongit_ok) { *nongit_ok = 1; return NULL; } die("Not a git repository: '%s'", gitdirenv); } if (!getcwd(cwd, sizeof(cwd)-1)) die_errno("Unable to read current working directory"); ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs); if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) ceil_offset = 1; /* * Test in the following order (relative to the cwd): * - .git (file containing "gitdir: <path>") * - .git/ * - ./ (bare) * - ../.git * - ../.git/ * - ../ (bare) * - ../../.git/ * etc. */ offset = len = strlen(cwd); for (;;) { gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT); if (gitfile_dir) { if (set_git_dir(gitfile_dir)) die("Repository setup failed"); break; } if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) break; if (is_git_directory(".")) { inside_git_dir = 1; if (!work_tree_env) inside_work_tree = 0; if (offset != len) { cwd[offset] = '\0'; setenv(GIT_DIR_ENVIRONMENT, cwd, 1); } else setenv(GIT_DIR_ENVIRONMENT, ".", 1); check_repository_format_gently(nongit_ok); return NULL; } while (--offset > ceil_offset && cwd[offset] != '/'); if (offset <= ceil_offset) { if (nongit_ok) { if (chdir(cwd)) die_errno("Cannot come back to cwd"); *nongit_ok = 1; return NULL; } die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT); } if (chdir("..")) die_errno("Cannot change to '%s/..'", cwd); } inside_git_dir = 0; if (!work_tree_env) inside_work_tree = 1; git_work_tree_cfg = xstrndup(cwd, offset); if (check_repository_format_gently(nongit_ok)) return NULL; if (offset == len) return NULL; /* Make "offset" point to past the '/', and add a '/' at the end */ offset++; cwd[len++] = '/'; cwd[len] = 0; return cwd + offset; }
int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(6 * sizeof(const char *)); int i, ref_count = 0; char buffer[1024]; struct rev_info revs; struct child_process rls; FILE *rls_fout; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *)); argv_boundary[0] = "rev-list"; argv_boundary[1] = "--boundary"; argv_boundary[2] = "--pretty=oneline"; argv_boundary[argc + 2] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_boundary; rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (fgets(buffer, sizeof(buffer), rls_fout)) { unsigned char sha1[20]; if (buffer[0] == '-') { write_or_die(bundle_fd, buffer, strlen(buffer)); if (!get_sha1_hex(buffer + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; add_pending_object(&revs, object, buffer); } } else if (!get_sha1_hex(buffer, sha1)) { struct object *object = parse_object(sha1); object->flags |= SHOWN; } } fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error("unrecognized argument: %s'", argv[1]); object_array_remove_duplicates(&revs.pending); for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; char *ref; const char *display_ref; int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) continue; if (!resolve_ref(e->name, sha1, 1, &flag)) flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; if (e->item->type == OBJ_TAG && !is_tag_in_date_range(e->item, &revs)) { e->item->flags |= UNINTERESTING; continue; } /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. * * Non commit objects such as tags and blobs do not have * this issue as they are not affected by those extra * constraints. */ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); free(ref); continue; } /* * If you run "git bundle create bndl v1.0..v2.0", the * name of the positive ref is "v2.0" but that is the * commit that is referenced by the tag, and not the tag * itself. */ if (hashcmp(sha1, e->item->sha1)) { /* * Is this the positive end of a range expressed * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ struct commit *one = lookup_commit_reference(sha1); struct object *obj; if (e->item == &(one->object)) { /* * Need to include e->name as an * independent ref to the pack-objects * input, so that the tag is included * in the output; otherwise we would * end up triggering "empty bundle" * error. */ obj = parse_object(sha1); obj->flags |= SHOWN; add_pending_object(&revs, obj, e->name); } free(ref); continue; } ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); free(ref); } if (!ref_count) die ("Refusing to create empty bundle."); /* end header */ write_or_die(bundle_fd, "\n", 1); /* write pack */ argv_pack[0] = "pack-objects"; argv_pack[1] = "--all-progress-implied"; argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; argv_pack[4] = "--delta-base-offset"; argv_pack[5] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_pack; rls.in = -1; rls.out = bundle_fd; rls.git_cmd = 1; if (start_command(&rls)) return error("Could not spawn pack-objects"); /* * start_command closed bundle_fd if it was > 1 * so set the lock fd to -1 so commit_lock_file() * won't fail trying to close it. */ lock.fd = -1; for (i = 0; i < revs.pending.nr; i++) { struct object *object = revs.pending.objects[i].item; if (object->flags & UNINTERESTING) write_or_die(rls.in, "^", 1); write_or_die(rls.in, sha1_to_hex(object->sha1), 40); write_or_die(rls.in, "\n", 1); } close(rls.in); if (finish_command(&rls)) return error ("pack-objects died"); if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno("cannot create '%s'", path); } return 0; }
int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) { const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; char *tz_c; int logfd, tz, reccnt = 0; struct stat st; unsigned long date; unsigned char logged_sha1[20]; void *log_mapped; size_t mapsz; logfile = git_path("logs/%s", ref); logfd = open(logfile, O_RDONLY, 0); if (logfd < 0) die_errno("Unable to read log '%s'", logfile); fstat(logfd, &st); if (!st.st_size) die("Log %s is empty.", logfile); mapsz = xsize_t(st.st_size); log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0); logdata = log_mapped; close(logfd); lastrec = NULL; rec = logend = logdata + st.st_size; while (logdata < rec) { reccnt++; if (logdata < rec && *(rec-1) == '\n') rec--; lastgt = NULL; while (logdata < rec && *(rec-1) != '\n') { rec--; if (*rec == '>') lastgt = rec; } if (!lastgt) die("Log %s is corrupt.", logfile); date = strtoul(lastgt + 1, &tz_c, 10); if (date <= at_time || cnt == 0) { tz = strtoul(tz_c, NULL, 10); if (msg) *msg = ref_msg(rec, logend); if (cutoff_time) *cutoff_time = date; if (cutoff_tz) *cutoff_tz = tz; if (cutoff_cnt) *cutoff_cnt = reccnt - 1; if (lastrec) { if (get_sha1_hex(lastrec, logged_sha1)) die("Log %s is corrupt.", logfile); if (get_sha1_hex(rec + 41, sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { warning("Log %s has gap after %s.", logfile, show_date(date, tz, DATE_RFC2822)); } } else if (date == at_time) { if (get_sha1_hex(rec + 41, sha1)) die("Log %s is corrupt.", logfile); } else { if (get_sha1_hex(rec + 41, logged_sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { warning("Log %s unexpectedly ended on %s.", logfile, show_date(date, tz, DATE_RFC2822)); } } munmap(log_mapped, mapsz); return 0; } lastrec = rec; if (cnt > 0) cnt--; } rec = logdata; while (rec < logend && *rec != '>' && *rec != '\n') rec++; if (rec == logend || *rec == '\n') die("Log %s is corrupt.", logfile); date = strtoul(rec + 1, &tz_c, 10); tz = strtoul(tz_c, NULL, 10); if (get_sha1_hex(logdata, sha1)) die("Log %s is corrupt.", logfile); if (is_null_sha1(sha1)) { if (get_sha1_hex(logdata + 41, sha1)) die("Log %s is corrupt.", logfile); } if (msg) *msg = ref_msg(logdata, logend); munmap(log_mapped, mapsz); if (cutoff_time) *cutoff_time = date; if (cutoff_tz) *cutoff_tz = tz; if (cutoff_cnt) *cutoff_cnt = reccnt; return 1; }
static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; struct ref dummy = {NULL}, *tail = &dummy; struct child_process rsync; const char *args[5]; int temp_dir_len; if (for_push) return NULL; /* copy the refs to the temporary directory */ strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX")); if (!mkdtemp(temp_dir.buf)) die_errno ("Could not make temporary directory"); temp_dir_len = temp_dir.len; strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/refs"); memset(&rsync, 0, sizeof(rsync)); rsync.argv = args; rsync.stdout_to_stderr = 1; args[0] = "rsync"; args[1] = (transport->verbose > 1) ? "-rv" : "-r"; args[2] = buf.buf; args[3] = temp_dir.buf; args[4] = NULL; if (run_command(&rsync)) die ("Could not run rsync to get refs"); strbuf_reset(&buf); strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/packed-refs"); args[2] = buf.buf; if (run_command(&rsync)) die ("Could not run rsync to get refs"); /* read the copied refs */ strbuf_addstr(&temp_dir, "/refs"); read_loose_refs(&temp_dir, temp_dir_len + 1, &tail); strbuf_setlen(&temp_dir, temp_dir_len); tail = &dummy; strbuf_addstr(&temp_dir, "/packed-refs"); insert_packed_refs(temp_dir.buf, &tail); strbuf_setlen(&temp_dir, temp_dir_len); if (remove_dir_recursively(&temp_dir, 0)) warning ("Error removing temporary directory %s.", temp_dir.buf); strbuf_release(&buf); strbuf_release(&temp_dir); return dummy.next; }
static int add_worktree(const char *path, const char *refname, const struct add_opts *opts) { struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; const char *name; struct stat st; struct child_process cp = CHILD_PROCESS_INIT; struct argv_array child_env = ARGV_ARRAY_INIT; int counter = 0, len, ret; struct strbuf symref = STRBUF_INIT; struct commit *commit = NULL; int is_branch = 0; if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); /* is 'refname' a branch or commit? */ if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && ref_exists(symref.buf)) { is_branch = 1; if (!opts->force) die_if_checked_out(symref.buf, 0); } commit = lookup_commit_reference_by_name(refname); if (!commit) die(_("invalid reference: %s"), refname); name = worktree_basename(path, &len); git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name); len = sb_repo.len; if (safe_create_leading_directories_const(sb_repo.buf)) die_errno(_("could not create leading directories of '%s'"), sb_repo.buf); while (!stat(sb_repo.buf, &st)) { counter++; strbuf_setlen(&sb_repo, len); strbuf_addf(&sb_repo, "%d", counter); } name = strrchr(sb_repo.buf, '/') + 1; junk_pid = getpid(); atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); if (mkdir(sb_repo.buf, 0777)) die_errno(_("could not create directory of '%s'"), sb_repo.buf); junk_git_dir = xstrdup(sb_repo.buf); is_junk = 1; /* * lock the incomplete repo so prune won't delete it, unlock * after the preparation is over. */ strbuf_addf(&sb, "%s/locked", sb_repo.buf); if (!opts->keep_locked) write_file(sb.buf, "initializing"); else write_file(sb.buf, "added with --lock"); strbuf_addf(&sb_git, "%s/.git", path); if (safe_create_leading_directories_const(sb_git.buf)) die_errno(_("could not create leading directories of '%s'"), sb_git.buf); junk_work_tree = xstrdup(path); strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); write_file(sb.buf, "%s", real_path(sb_git.buf)); write_file(sb_git.buf, "gitdir: %s/worktrees/%s", real_path(get_git_common_dir()), name); /* * This is to keep resolve_ref() happy. We need a valid HEAD * or is_git_directory() will reject the directory. Any value which * looks like an object ID will do since it will be immediately * replaced by the symbolic-ref or update-ref invocation in the new * worktree. */ strbuf_reset(&sb); strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); write_file(sb.buf, "%s", sha1_to_hex(null_sha1)); strbuf_reset(&sb); strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name); argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); cp.git_cmd = 1; if (!is_branch) argv_array_pushl(&cp.args, "update-ref", "HEAD", oid_to_hex(&commit->object.oid), NULL); else argv_array_pushl(&cp.args, "symbolic-ref", "HEAD", symref.buf, NULL); cp.env = child_env.argv; ret = run_command(&cp); if (ret) goto done; if (opts->checkout) { cp.argv = NULL; argv_array_clear(&cp.args); argv_array_pushl(&cp.args, "reset", "--hard", NULL); cp.env = child_env.argv; ret = run_command(&cp); if (ret) goto done; } is_junk = 0; FREE_AND_NULL(junk_work_tree); FREE_AND_NULL(junk_git_dir); done: if (ret || !opts->keep_locked) { strbuf_reset(&sb); strbuf_addf(&sb, "%s/locked", sb_repo.buf); unlink_or_warn(sb.buf); } /* * Hook failure does not warrant worktree deletion, so run hook after * is_junk is cleared, but do return appropriate code when hook fails. */ if (!ret && opts->checkout) { const char *hook = find_hook("post-checkout"); if (hook) { const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL }; cp.git_cmd = 0; cp.no_stdin = 1; cp.stdout_to_stderr = 1; cp.dir = path; cp.env = env; cp.argv = NULL; argv_array_pushl(&cp.args, absolute_path(hook), oid_to_hex(&null_oid), oid_to_hex(&commit->object.oid), "1", NULL); ret = run_command(&cp); } } argv_array_clear(&child_env); strbuf_release(&sb); strbuf_release(&symref); strbuf_release(&sb_repo); strbuf_release(&sb_git); return ret; }
static int rsync_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; int result = 0, i; struct child_process rsync; const char *args[10]; if (flags & TRANSPORT_PUSH_MIRROR) return error("rsync transport does not support mirror mode"); /* first push the objects */ strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addch(&buf, '/'); memset(&rsync, 0, sizeof(rsync)); rsync.argv = args; rsync.stdout_to_stderr = 1; i = 0; args[i++] = "rsync"; args[i++] = "-a"; if (flags & TRANSPORT_PUSH_DRY_RUN) args[i++] = "--dry-run"; if (transport->verbose > 1) args[i++] = "-v"; args[i++] = "--ignore-existing"; args[i++] = "--exclude"; args[i++] = "info"; args[i++] = get_object_directory(); args[i++] = buf.buf; args[i++] = NULL; if (run_command(&rsync)) return error("Could not push objects to %s", rsync_url(transport->url)); /* copy the refs to the temporary directory; they could be packed. */ strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX")); if (!mkdtemp(temp_dir.buf)) die_errno ("Could not make temporary directory"); strbuf_addch(&temp_dir, '/'); if (flags & TRANSPORT_PUSH_ALL) { if (for_each_ref(write_one_ref, &temp_dir)) return -1; } else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec)) return -1; i = 2; if (flags & TRANSPORT_PUSH_DRY_RUN) args[i++] = "--dry-run"; if (!(flags & TRANSPORT_PUSH_FORCE)) args[i++] = "--ignore-existing"; args[i++] = temp_dir.buf; args[i++] = rsync_url(transport->url); args[i++] = NULL; if (run_command(&rsync)) result = error("Could not push to %s", rsync_url(transport->url)); if (remove_dir_recursively(&temp_dir, 0)) warning ("Could not remove temporary directory %s.", temp_dir.buf); strbuf_release(&buf); strbuf_release(&temp_dir); return result; }
static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, const char *src_repo, int src_baselen) { struct dirent *de; struct stat buf; int src_len, dest_len; DIR *dir; dir = opendir(src->buf); if (!dir) die_errno(_("failed to open '%s'"), src->buf); if (mkdir(dest->buf, 0777)) { if (errno != EEXIST) die_errno(_("failed to create directory '%s'"), dest->buf); else if (stat(dest->buf, &buf)) die_errno(_("failed to stat '%s'"), dest->buf); else if (!S_ISDIR(buf.st_mode)) die(_("%s exists and is not a directory"), dest->buf); } strbuf_addch(src, '/'); src_len = src->len; strbuf_addch(dest, '/'); dest_len = dest->len; while ((de = readdir(dir)) != NULL) { strbuf_setlen(src, src_len); strbuf_addstr(src, de->d_name); strbuf_setlen(dest, dest_len); strbuf_addstr(dest, de->d_name); if (stat(src->buf, &buf)) { warning (_("failed to stat %s\n"), src->buf); continue; } if (S_ISDIR(buf.st_mode)) { if (de->d_name[0] != '.') copy_or_link_directory(src, dest, src_repo, src_baselen); continue; } /* Files that cannot be copied bit-for-bit... */ if (!strcmp(src->buf + src_baselen, "/info/alternates")) { copy_alternates(src, dest, src_repo); continue; } if (unlink(dest->buf) && errno != ENOENT) die_errno(_("failed to unlink '%s'"), dest->buf); if (!option_no_hardlinks) { if (!link(src->buf, dest->buf)) continue; if (option_local > 0) die_errno(_("failed to create link '%s'"), dest->buf); option_no_hardlinks = 1; } if (copy_file_with_time(dest->buf, src->buf, 0666)) die_errno(_("failed to copy file to '%s'"), dest->buf); } closedir(dir); }
static const char *setup_explicit_git_dir(const char *gitdirenv, char *cwd, int len, int *nongit_ok) { const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); const char *worktree; char *gitfile; int offset; if (PATH_MAX - 40 < strlen(gitdirenv)) die("'$%s' too big", GIT_DIR_ENVIRONMENT); gitfile = (char*)read_gitfile(gitdirenv); if (gitfile) { gitfile = xstrdup(gitfile); gitdirenv = gitfile; } if (!is_git_directory(gitdirenv)) { if (nongit_ok) { *nongit_ok = 1; free(gitfile); return NULL; } die("Not a git repository: '%s'", gitdirenv); } if (check_repository_format_gently(gitdirenv, nongit_ok)) { free(gitfile); return NULL; } /* #3, #7, #11, #15, #19, #23, #27, #31 (see t1510) */ if (work_tree_env) set_git_work_tree(work_tree_env); else if (is_bare_repository_cfg > 0) { if (git_work_tree_cfg) /* #22.2, #30 */ die("core.bare and core.worktree do not make sense"); /* #18, #26 */ set_git_dir(gitdirenv); free(gitfile); return NULL; } else if (git_work_tree_cfg) { /* #6, #14 */ if (is_absolute_path(git_work_tree_cfg)) set_git_work_tree(git_work_tree_cfg); else { char core_worktree[PATH_MAX]; if (chdir(gitdirenv)) die_errno("Could not chdir to '%s'", gitdirenv); if (chdir(git_work_tree_cfg)) die_errno("Could not chdir to '%s'", git_work_tree_cfg); if (!getcwd(core_worktree, PATH_MAX)) die_errno("Could not get directory '%s'", git_work_tree_cfg); if (chdir(cwd)) die_errno("Could not come back to cwd"); set_git_work_tree(core_worktree); } } else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) { /* #16d */ set_git_dir(gitdirenv); free(gitfile); return NULL; } else /* #2, #10 */ set_git_work_tree("."); /* set_git_work_tree() must have been called by now */ worktree = get_git_work_tree(); /* both get_git_work_tree() and cwd are already normalized */ if (!strcmp(cwd, worktree)) { /* cwd == worktree */ set_git_dir(gitdirenv); free(gitfile); return NULL; } offset = dir_inside_of(cwd, worktree); if (offset >= 0) { /* cwd inside worktree? */ set_git_dir(real_path(gitdirenv)); if (chdir(worktree)) die_errno("Could not chdir to '%s'", worktree); cwd[len++] = '/'; cwd[len] = '\0'; free(gitfile); return cwd + offset; } /* cwd outside worktree */ set_git_dir(gitdirenv); free(gitfile); return NULL; }
int chk_zero(int result, char *msg) { if (result != 0) die_errno("%s", msg); return result; }
/* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. */ static const char *setup_git_directory_gently_1(int *nongit_ok) { const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; static char cwd[PATH_MAX + 1]; const char *gitdirenv, *ret; char *gitfile; int len, offset, offset_parent, ceil_offset = -1; dev_t current_device = 0; int one_filesystem = 1; /* * Let's assume that we are in a git repository. * If it turns out later that we are somewhere else, the value will be * updated accordingly. */ if (nongit_ok) *nongit_ok = 0; if (!getcwd(cwd, sizeof(cwd) - 1)) die_errno("Unable to read current working directory"); offset = len = strlen(cwd); /* * If GIT_DIR is set explicitly, we're not going * to do any discovery, but we still do repository * validation. */ gitdirenv = getenv(GIT_DIR_ENVIRONMENT); if (gitdirenv) return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok); if (env_ceiling_dirs) { int empty_entry_found = 0; string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1); filter_string_list(&ceiling_dirs, 0, canonicalize_ceiling_entry, &empty_entry_found); ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs); string_list_clear(&ceiling_dirs, 0); } if (ceil_offset < 0 && has_dos_drive_prefix(cwd)) ceil_offset = 1; /* * Test in the following order (relative to the cwd): * - .git (file containing "gitdir: <path>") * - .git/ * - ./ (bare) * - ../.git * - ../.git/ * - ../ (bare) * - ../../.git/ * etc. */ one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0); if (one_filesystem) current_device = get_device_or_die(".", NULL, 0); for (;;) { gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT); if (gitfile) gitdirenv = gitfile = xstrdup(gitfile); else { if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT)) gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT; } if (gitdirenv) { ret = setup_discovered_git_dir(gitdirenv, cwd, offset, len, nongit_ok); free(gitfile); return ret; } free(gitfile); if (is_git_directory(".")) return setup_bare_git_dir(cwd, offset, len, nongit_ok); offset_parent = offset; while (--offset_parent > ceil_offset && cwd[offset_parent] != '/'); if (offset_parent <= ceil_offset) return setup_nongit(cwd, nongit_ok); if (one_filesystem) { dev_t parent_device = get_device_or_die("..", cwd, offset); if (parent_device != current_device) { if (nongit_ok) { if (chdir(cwd)) die_errno("Cannot come back to cwd"); *nongit_ok = 1; return NULL; } cwd[offset] = '\0'; die("Not a git repository (or any parent up to mount point %s)\n" "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd); } } if (chdir("..")) { cwd[offset] = '\0'; die_errno("Cannot change to '%s/..'", cwd); } offset = offset_parent; } }
int chk_non_negative(int result, char *msg) { if (result < 0) die_errno("%s", msg); return result; }
/* Get the name for the merge commit's message. */ static void merge_name(const char *remote, struct strbuf *msg) { struct object *remote_head; unsigned char branch_head[20], buf_sha[20]; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; const char *ptr; char *found_ref; int len, early; strbuf_branchname(&bname, remote); remote = bname.buf; memset(branch_head, 0, sizeof(branch_head)); remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT); if (!remote_head) die("'%s' does not point to a commit", remote); if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) { if (!prefixcmp(found_ref, "refs/heads/")) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } if (!prefixcmp(found_ref, "refs/remotes/")) { strbuf_addf(msg, "%s\t\tremote branch '%s' of .\n", sha1_to_hex(branch_head), remote); goto cleanup; } } /* See if remote matches <name>^^^.. or <name>~<number> */ for (len = 0, ptr = remote + strlen(remote); remote < ptr && ptr[-1] == '^'; ptr--) len++; if (len) early = 1; else { early = 0; ptr = strrchr(remote, '~'); if (ptr) { int seen_nonzero = 0; len++; /* count ~ */ while (*++ptr && isdigit(*ptr)) { seen_nonzero |= (*ptr != '0'); len++; } if (*ptr) len = 0; /* not ...~<number> */ else if (seen_nonzero) early = 1; else if (len == 1) early = 1; /* "name~" is "name~1"! */ } } if (len) { struct strbuf truname = STRBUF_INIT; strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); strbuf_setlen(&truname, truname.len - len); if (resolve_ref(truname.buf, buf_sha, 0, NULL)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->sha1), truname.buf + 11, (early ? " (early part)" : "")); strbuf_release(&truname); goto cleanup; } } if (!strcmp(remote, "FETCH_HEAD") && !access(git_path("FETCH_HEAD"), R_OK)) { FILE *fp; struct strbuf line = STRBUF_INIT; char *ptr; fp = fopen(git_path("FETCH_HEAD"), "r"); if (!fp) die_errno("could not open '%s' for reading", git_path("FETCH_HEAD")); strbuf_getline(&line, fp, '\n'); fclose(fp); ptr = strstr(line.buf, "\tnot-for-merge\t"); if (ptr) strbuf_remove(&line, ptr-line.buf+1, 13); strbuf_addbuf(msg, &line); strbuf_release(&line); goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", sha1_to_hex(remote_head->sha1), remote); cleanup: strbuf_release(&buf); strbuf_release(&bname); }