int cmd_reset(int argc, const char **argv, const char *prefix) { int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0; int patch_mode = 0; const char *rev = "HEAD"; unsigned char sha1[20], *orig = NULL, sha1_orig[20], *old_orig = NULL, sha1_old_orig[20]; struct commit *commit; struct strbuf msg = STRBUF_INIT; const struct option options[] = { OPT__QUIET(&quiet, "be quiet, only report errors"), OPT_SET_INT(0, "mixed", &reset_type, "reset HEAD and index", MIXED), OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT), OPT_SET_INT(0, "hard", &reset_type, "reset HEAD, index and working tree", HARD), OPT_SET_INT(0, "merge", &reset_type, "reset HEAD, index and working tree", MERGE), OPT_SET_INT(0, "keep", &reset_type, "reset HEAD but keep local changes", KEEP), OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); /* * Possible arguments are: * * git reset [-opts] <rev> <paths>... * git reset [-opts] <rev> -- <paths>... * git reset [-opts] -- <paths>... * git reset [-opts] <paths>... * * At this point, argv[i] points immediately after [-opts]. */ if (i < argc) { if (!strcmp(argv[i], "--")) { i++; /* reset to HEAD, possibly with paths */ } else if (i + 1 < argc && !strcmp(argv[i+1], "--")) { rev = argv[i]; i += 2; } /* * Otherwise, argv[i] could be either <rev> or <paths> and * has to be unambiguous. */ else if (!get_sha1(argv[i], sha1)) { /* * Ok, argv[i] looks like a rev; it should not * be a filename. */ verify_non_filename(prefix, argv[i]); rev = argv[i++]; } else { /* Otherwise we treat this as a filename */ verify_filename(prefix, argv[i]); } } if (get_sha1(rev, sha1)) die(_("Failed to resolve '%s' as a valid ref."), rev); commit = lookup_commit_reference(sha1); if (!commit) die(_("Could not parse object '%s'."), rev); hashcpy(sha1, commit->object.sha1); if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); return interactive_reset(rev, argv + i, prefix); } /* git reset tree [--] paths... can be used to * load chosen paths from the tree into the index without * affecting the working tree nor HEAD. */ if (i < argc) { if (reset_type == MIXED) warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.")); else if (reset_type != NONE) die(_("Cannot do %s reset with paths."), _(reset_type_names[reset_type])); return read_from_tree(prefix, argv + i, sha1, quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN); } if (reset_type == NONE) reset_type = MIXED; /* by default */ if (reset_type != SOFT && reset_type != MIXED) setup_work_tree(); if (reset_type == MIXED && is_bare_repository()) die(_("%s reset is not allowed in a bare repository"), _(reset_type_names[reset_type])); /* Soft reset does not touch the index file nor the working tree * at all, but requires them in a good order. Other resets reset * the index file to the tree object we are switching to. */ if (reset_type == SOFT) die_if_unmerged_cache(reset_type); else { int err; if (reset_type == KEEP) die_if_unmerged_cache(reset_type); err = reset_index_file(sha1, reset_type, quiet); if (reset_type == KEEP) err = err || reset_index_file(sha1, MIXED, quiet); if (err) die(_("Could not reset index file to revision '%s'."), rev); } /* Any resets update HEAD to the head being switched to, * saving the previous head in ORIG_HEAD before. */ if (!get_sha1("ORIG_HEAD", sha1_old_orig)) old_orig = sha1_old_orig; if (!get_sha1("HEAD", sha1_orig)) { orig = sha1_orig; set_reflog_message(&msg, "updating ORIG_HEAD", NULL); update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR); } else if (old_orig) delete_ref("ORIG_HEAD", old_orig, 0); set_reflog_message(&msg, "updating HEAD", rev); update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR); switch (reset_type) { case HARD: if (!update_ref_status && !quiet) print_new_head_line(commit); break; case SOFT: /* Nothing else to do. */ break; case MIXED: /* Report what has not been updated. */ update_index_refresh(0, NULL, quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN); break; } remove_branch_state(); strbuf_release(&msg); return update_ref_status; }
static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1) { ALLOC_GROW(extra->array, extra->nr + 1, extra->alloc); hashcpy(&(extra->array[extra->nr][0]), sha1); extra->nr++; }
int rename_ref(const char *oldref, const char *newref, const char *logmsg) { static const char renamed_ref[] = "RENAMED-REF"; unsigned char sha1[20], orig_sha1[20]; int flag = 0, logmoved = 0; struct ref_lock *lock; struct stat loginfo; int log = !lstat(git_path("logs/%s", oldref), &loginfo); const char *symref = NULL; if (log && S_ISLNK(loginfo.st_mode)) return error("reflog for %s is a symlink", oldref); symref = resolve_ref(oldref, orig_sha1, 1, &flag); if (flag & REF_ISSYMREF) return error("refname %s is a symbolic ref, renaming it is not supported", oldref); if (!symref) return error("refname %s not found", oldref); if (!is_refname_available(newref, oldref, get_packed_refs(NULL), 0)) return 1; if (!is_refname_available(newref, oldref, get_loose_refs(NULL), 0)) return 1; lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL); if (!lock) return error("unable to lock %s", renamed_ref); lock->force_write = 1; if (write_ref_sha1(lock, orig_sha1, logmsg)) return error("unable to save current sha1 in %s", renamed_ref); if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG))) return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s", oldref, strerror(errno)); if (delete_ref(oldref, orig_sha1, REF_NODEREF)) { error("unable to delete old %s", oldref); goto rollback; } if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) { if (errno==EISDIR) { if (remove_empty_directories(git_path("%s", newref))) { error("Directory not empty: %s", newref); goto rollback; } } else { error("unable to delete existing %s", newref); goto rollback; } } if (log && safe_create_leading_directories(git_path("logs/%s", newref))) { error("unable to create directory for %s", newref); goto rollback; } retry: if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) { if (errno==EISDIR || errno==ENOTDIR) { /* * rename(a, b) when b is an existing * directory ought to result in ISDIR, but * Solaris 5.8 gives ENOTDIR. Sheesh. */ if (remove_empty_directories(git_path("logs/%s", newref))) { error("Directory not empty: logs/%s", newref); goto rollback; } goto retry; } else { error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s", newref, strerror(errno)); goto rollback; } } logmoved = log; lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); if (!lock) { error("unable to lock %s for update", newref); goto rollback; } lock->force_write = 1; hashcpy(lock->old_sha1, orig_sha1); if (write_ref_sha1(lock, orig_sha1, logmsg)) { error("unable to write current sha1 into %s", newref); goto rollback; } return 0; rollback: lock = lock_ref_sha1_basic(oldref, NULL, 0, NULL); if (!lock) { error("unable to lock %s for rollback", oldref); goto rollbacklog; } lock->force_write = 1; flag = log_all_ref_updates; log_all_ref_updates = 0; if (write_ref_sha1(lock, orig_sha1, NULL)) error("unable to write current sha1 into %s", oldref); log_all_ref_updates = flag; rollbacklog: if (logmoved && rename(git_path("logs/%s", newref), git_path("logs/%s", oldref))) error("unable to restore logfile %s from %s: %s", oldref, newref, strerror(errno)); if (!logmoved && log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref))) error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s", oldref, strerror(errno)); return 1; }
int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; int patch_mode = 0, nul_term_line = 0, read_from_stdin = 0, unborn; char **stdin_paths = NULL; int stdin_nr = 0, stdin_alloc = 0; const char *rev; struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, N_("reset HEAD and index"), MIXED), OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT), OPT_SET_INT(0, "hard", &reset_type, N_("reset HEAD, index and working tree"), HARD), OPT_SET_INT(0, "merge", &reset_type, N_("reset HEAD, index and working tree"), MERGE), OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "reset", "control recursive updating of submodules", PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater }, OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), OPT_BOOL('z', NULL, &nul_term_line, N_("EXPERIMENTAL: paths are separated with NUL character")), OPT_BOOL(0, "stdin", &read_from_stdin, N_("EXPERIMENTAL: read paths from <stdin>")), OPT_END() }; git_config(git_reset_config, NULL); argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); if (read_from_stdin) { strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; int flags = PATHSPEC_PREFER_FULL; struct strbuf buf = STRBUF_INIT; struct strbuf unquoted = STRBUF_INIT; if (patch_mode) die(_("--stdin is incompatible with --patch")); if (pathspec.nr) die(_("--stdin is incompatible with path arguments")); while (getline_fn(&buf, stdin) != EOF) { if (!nul_term_line && buf.buf[0] == '"') { strbuf_reset(&unquoted); if (unquote_c_style(&unquoted, buf.buf, NULL)) die(_("line is badly quoted")); strbuf_swap(&buf, &unquoted); } ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc); stdin_paths[stdin_nr++] = xstrdup(buf.buf); strbuf_reset(&buf); } strbuf_release(&unquoted); strbuf_release(&buf); ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc); stdin_paths[stdin_nr++] = NULL; flags |= PATHSPEC_LITERAL_PATH; parse_pathspec(&pathspec, 0, flags, prefix, (const char **)stdin_paths); } else if (nul_term_line) die(_("-z requires --stdin")); unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN); } else if (!pathspec.nr) { struct commit *commit; if (get_oid_committish(rev, &oid)) die(_("Failed to resolve '%s' as a valid revision."), rev); commit = lookup_commit_reference(&oid); if (!commit) die(_("Could not parse object '%s'."), rev); oidcpy(&oid, &commit->object.oid); } else { struct tree *tree; if (get_oid_treeish(rev, &oid)) die(_("Failed to resolve '%s' as a valid tree."), rev); tree = parse_tree_indirect(&oid); if (!tree) die(_("Could not parse object '%s'."), rev); oidcpy(&oid, &tree->object.oid); } if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); return run_add_interactive(rev, "--patch=reset", &pathspec); } /* git reset tree [--] paths... can be used to * load chosen paths from the tree into the index without * affecting the working tree nor HEAD. */ if (pathspec.nr) { if (reset_type == MIXED) warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.")); else if (reset_type != NONE) die(_("Cannot do %s reset with paths."), _(reset_type_names[reset_type])); } if (reset_type == NONE) reset_type = MIXED; /* by default */ if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree())) setup_work_tree(); if (reset_type == MIXED && is_bare_repository()) die(_("%s reset is not allowed in a bare repository"), _(reset_type_names[reset_type])); if (intent_to_add && reset_type != MIXED) die(_("-N can only be used with --mixed")); /* Soft reset does not touch the index file nor the working tree * at all, but requires them in a good order. Other resets reset * the index file to the tree object we are switching to. */ if (reset_type == SOFT || reset_type == KEEP) die_if_unmerged_cache(reset_type); if (reset_type != SOFT) { struct lock_file lock = LOCK_INIT; hold_locked_index(&lock, LOCK_DIE_ON_ERROR); if (reset_type == MIXED) { int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; if (read_from_tree(&pathspec, &oid, intent_to_add)) return 1; if (get_git_work_tree()) refresh_index(&the_index, flags, NULL, NULL, _("Unstaged changes after reset:")); } else { int err = reset_index(&oid, reset_type, quiet); if (reset_type == KEEP && !err) err = reset_index(&oid, MIXED, quiet); if (err) die(_("Could not reset index file to revision '%s'."), rev); } if (write_locked_index(&the_index, &lock, COMMIT_LOCK)) die(_("Could not write new index file.")); } if (!pathspec.nr && !unborn) { /* Any resets without paths update HEAD to the head being * switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status = reset_refs(rev, &oid); if (reset_type == HARD && !update_ref_status && !quiet) print_new_head_line(lookup_commit_reference(&oid)); } if (!pathspec.nr) remove_branch_state(); if (stdin_paths) { while (stdin_nr) free(stdin_paths[--stdin_nr]); free(stdin_paths); } return update_ref_status; }
static int deflate_to_pack(struct bulk_checkin_state *state, unsigned char result_sha1[], int fd, size_t size, enum object_type type, const char *path, unsigned flags) { off_t seekback, already_hashed_to; git_SHA_CTX ctx; unsigned char obuf[16384]; unsigned header_len; struct sha1file_checkpoint checkpoint; struct pack_idx_entry *idx = NULL; seekback = lseek(fd, 0, SEEK_CUR); if (seekback == (off_t) -1) return error("cannot find the current offset"); header_len = xsnprintf((char *)obuf, sizeof(obuf), "%s %" PRIuMAX, typename(type), (uintmax_t)size) + 1; git_SHA1_Init(&ctx); git_SHA1_Update(&ctx, obuf, header_len); /* Note: idx is non-NULL when we are writing */ if ((flags & HASH_WRITE_OBJECT) != 0) idx = xcalloc(1, sizeof(*idx)); already_hashed_to = 0; while (1) { prepare_to_stream(state, flags); if (idx) { sha1file_checkpoint(state->f, &checkpoint); idx->offset = state->offset; crc32_begin(state->f); } if (!stream_to_pack(state, &ctx, &already_hashed_to, fd, size, type, path, flags)) break; /* * Writing this object to the current pack will make * it too big; we need to truncate it, start a new * pack, and write into it. */ if (!idx) die("BUG: should not happen"); sha1file_truncate(state->f, &checkpoint); state->offset = checkpoint.offset; finish_bulk_checkin(state); if (lseek(fd, seekback, SEEK_SET) == (off_t) -1) return error("cannot seek back"); } git_SHA1_Final(result_sha1, &ctx); if (!idx) return 0; idx->crc32 = crc32_end(state->f); if (already_written(state, result_sha1)) { sha1file_truncate(state->f, &checkpoint); state->offset = checkpoint.offset; free(idx); } else { hashcpy(idx->sha1, result_sha1); ALLOC_GROW(state->written, state->nr_written + 1, state->alloc_written); state->written[state->nr_written++] = idx; } return 0; }
static int handle_line(char *line, struct merge_parents *merge_parents) { int i, len = strlen(line); struct origin_data *origin_data; char *src; const char *origin; struct src_data *src_data; struct string_list_item *item; int pulling_head = 0; unsigned char sha1[20]; if (len < 43 || line[40] != '\t') return 1; if (starts_with(line + 41, "not-for-merge")) return 0; if (line[41] != '\t') return 2; i = get_sha1_hex(line, sha1); if (i) return 3; if (!find_merge_parent(merge_parents, sha1, NULL)) return 0; /* subsumed by other parents */ origin_data = xcalloc(1, sizeof(struct origin_data)); hashcpy(origin_data->sha1, sha1); if (line[len - 1] == '\n') line[len - 1] = 0; line += 42; /* * At this point, line points at the beginning of comment e.g. * "branch 'frotz' of git://that/repository.git". * Find the repository name and point it with src. */ src = strstr(line, " of "); if (src) { *src = 0; src += 4; pulling_head = 0; } else { src = line; pulling_head = 1; } item = unsorted_string_list_lookup(&srcs, src); if (!item) { item = string_list_append(&srcs, src); item->util = xcalloc(1, sizeof(struct src_data)); init_src_data(item->util); } src_data = item->util; if (pulling_head) { origin = src; src_data->head_status |= 1; } else if (starts_with(line, "branch ")) { origin_data->is_local_branch = 1; origin = line + 7; string_list_append(&src_data->branch, origin); src_data->head_status |= 2; } else if (starts_with(line, "tag ")) { origin = line; string_list_append(&src_data->tag, origin + 4); src_data->head_status |= 2; } else if (skip_prefix(line, "remote-tracking branch ", &origin)) { string_list_append(&src_data->r_branch, origin); src_data->head_status |= 2; } else { origin = src; string_list_append(&src_data->generic, line); src_data->head_status |= 2; } if (!strcmp(".", src) || !strcmp(src, origin)) { int len = strlen(origin); if (origin[0] == '\'' && origin[len - 1] == '\'') origin = xmemdupz(origin + 1, len - 2); } else origin = xstrfmt("%s of %s", origin, src); if (strcmp(".", src)) origin_data->is_local_branch = 0; string_list_append(&origins, origin)->util = origin_data; return 0; }
void cgit_print_patch(const char *new_rev, const char *old_rev, const char *prefix) { struct rev_info rev; struct commit *commit; unsigned char new_rev_sha1[20], old_rev_sha1[20]; char rev_range[2 * 40 + 3]; char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range }; char *patchname; if (!new_rev) new_rev = ctx.qry.head; if (get_sha1(new_rev, new_rev_sha1)) { cgit_print_error("Bad object id: %s", new_rev); return; } commit = lookup_commit_reference(new_rev_sha1); if (!commit) { cgit_print_error("Bad commit reference: %s", new_rev); return; } if (old_rev) { if (get_sha1(old_rev, old_rev_sha1)) { cgit_print_error("Bad object id: %s", old_rev); return; } if (!lookup_commit_reference(old_rev_sha1)) { cgit_print_error("Bad commit reference: %s", old_rev); return; } } else if (commit->parents && commit->parents->item) { hashcpy(old_rev_sha1, commit->parents->item->object.sha1); } else { hashclr(old_rev_sha1); } if (is_null_sha1(old_rev_sha1)) { memcpy(rev_range, sha1_to_hex(new_rev_sha1), 41); } else { sprintf(rev_range, "%s..%s", sha1_to_hex(old_rev_sha1), sha1_to_hex(new_rev_sha1)); } patchname = fmt("%s.patch", rev_range); ctx.page.mimetype = "text/plain"; ctx.page.filename = patchname; cgit_print_http_headers(); if (ctx.cfg.noplainemail) { rev_argv[2] = "--format=format:From %H Mon Sep 17 00:00:00 " "2001%nFrom: %an%nDate: %aD%n%w(78,0,1)Subject: " "%s%n%n%w(0)%b"; } init_revisions(&rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.verbose_header = 1; rev.diff = 1; rev.show_root_diff = 1; rev.max_parents = 1; rev.diffopt.output_format |= DIFF_FORMAT_PATCH; setup_revisions(ARRAY_SIZE(rev_argv), (const char **)rev_argv, &rev, NULL); prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { log_tree_commit(&rev, commit); printf("-- \ncgit %s\n\n", cgit_version); } fflush(stdout); }
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 (prefixcmp(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); 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); }
static int push_refs(struct transport *transport, struct ref *remote_refs, int flags) { int force_all = flags & TRANSPORT_PUSH_FORCE; int mirror = flags & TRANSPORT_PUSH_MIRROR; struct helper_data *data = transport->data; struct strbuf buf = STRBUF_INIT; struct child_process *helper; struct ref *ref; if (!remote_refs) return 0; helper = get_helper(transport); if (!data->push) return 1; for (ref = remote_refs; ref; ref = ref->next) { if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); else if (!mirror) continue; ref->deletion = is_null_sha1(ref->new_sha1); if (!ref->deletion && !hashcmp(ref->old_sha1, ref->new_sha1)) { ref->status = REF_STATUS_UPTODATE; continue; } if (force_all) ref->force = 1; strbuf_addstr(&buf, "push "); if (!ref->deletion) { if (ref->force) strbuf_addch(&buf, '+'); if (ref->peer_ref) strbuf_addstr(&buf, ref->peer_ref->name); else strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1)); } strbuf_addch(&buf, ':'); strbuf_addstr(&buf, ref->name); strbuf_addch(&buf, '\n'); } if (buf.len == 0) return 0; transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0; standard_options(transport); if (flags & TRANSPORT_PUSH_DRY_RUN) { if (set_helper_option(transport, "dry-run", "true") != 0) die("helper %s does not support dry-run", data->name); } strbuf_addch(&buf, '\n'); if (write_in_full(helper->in, buf.buf, buf.len) != buf.len) exit(128); ref = remote_refs; while (1) { char *refname, *msg; int status; strbuf_reset(&buf); if (strbuf_getline(&buf, data->out, '\n') == EOF) exit(128); /* child died, message supplied already */ if (!buf.len) break; if (!prefixcmp(buf.buf, "ok ")) { status = REF_STATUS_OK; refname = buf.buf + 3; } else if (!prefixcmp(buf.buf, "error ")) { status = REF_STATUS_REMOTE_REJECT; refname = buf.buf + 6; } else die("expected ok/error, helper said '%s'\n", buf.buf); msg = strchr(refname, ' '); if (msg) { struct strbuf msg_buf = STRBUF_INIT; const char *end; *msg++ = '\0'; if (!unquote_c_style(&msg_buf, msg, &end)) msg = strbuf_detach(&msg_buf, NULL); else msg = xstrdup(msg); strbuf_release(&msg_buf); if (!strcmp(msg, "no match")) { status = REF_STATUS_NONE; free(msg); msg = NULL; } else if (!strcmp(msg, "up to date")) { status = REF_STATUS_UPTODATE; free(msg); msg = NULL; } else if (!strcmp(msg, "non-fast forward")) { status = REF_STATUS_REJECT_NONFASTFORWARD; free(msg); msg = NULL; } } if (ref) ref = find_ref_by_name(ref, refname); if (!ref) ref = find_ref_by_name(remote_refs, refname); if (!ref) { warning("helper reported unexpected status of %s", refname); continue; } ref->status = status; ref->remote_status = msg; } strbuf_release(&buf); return 0; }
static int cat_one_file(int opt, const char *exp_type, const char *obj_name) { unsigned char sha1[20]; enum object_type type; char *buf; unsigned long size; struct object_context obj_context; if (get_sha1_with_context(obj_name, 0, sha1, &obj_context)) die("Not a valid object name %s", obj_name); buf = NULL; switch (opt) { case 't': type = sha1_object_info(sha1, NULL); if (type > 0) { printf("%s\n", typename(type)); return 0; } break; case 's': type = sha1_object_info(sha1, &size); if (type > 0) { printf("%lu\n", size); return 0; } break; case 'e': return !has_sha1_file(sha1); case 'p': type = sha1_object_info(sha1, NULL); if (type < 0) die("Not a valid object name %s", obj_name); /* custom pretty-print here */ if (type == OBJ_TREE) { const char *ls_args[3] = { NULL }; ls_args[0] = "ls-tree"; ls_args[1] = obj_name; return cmd_ls_tree(2, ls_args, NULL); } if (type == OBJ_BLOB) return stream_blob_to_fd(1, sha1, NULL, 0); buf = read_sha1_file(sha1, &type, &size); if (!buf) die("Cannot read object %s", obj_name); /* otherwise just spit out the data */ break; case 'c': if (!obj_context.path[0]) die("git cat-file --textconv %s: <object> must be <sha1:path>", obj_name); if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size)) die("git cat-file --textconv: unable to run textconv on %s", obj_name); break; case 0: if (type_from_string(exp_type) == OBJ_BLOB) { unsigned char blob_sha1[20]; if (sha1_object_info(sha1, NULL) == OBJ_TAG) { enum object_type type; unsigned long size; char *buffer = read_sha1_file(sha1, &type, &size); if (memcmp(buffer, "object ", 7) || get_sha1_hex(buffer + 7, blob_sha1)) die("%s not a valid tag", sha1_to_hex(sha1)); free(buffer); } else hashcpy(blob_sha1, sha1); if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB) return stream_blob_to_fd(1, blob_sha1, NULL, 0); /* * we attempted to dereference a tag to a blob * and failed; there may be new dereference * mechanisms this code is not aware of. * fall-back to the usual case. */ } buf = read_object_with_reference(sha1, exp_type, &size, NULL); break; default: die("git cat-file: unknown option: %s", exp_type); }
static void sha1_array_push(struct sha1_array *array, const unsigned char *sha1) { ALLOC_GROW(array->sha1, array->sha1_nr + 1, array->sha1_alloc); hashcpy(array->sha1[array->sha1_nr++], sha1); }
static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent) { struct diff_queue_struct *q = &diff_queued_diff; struct combine_diff_path *p, **tail = &curr; int i, cmp; if (!n) { for (i = 0; i < q->nr; i++) { int len; const char *path; if (diff_unmodified_pair(q->queue[i])) continue; path = q->queue[i]->two->path; len = strlen(path); p = xmalloc(combine_diff_path_size(num_parent, len)); p->path = (char *) &(p->parent[num_parent]); memcpy(p->path, path, len); p->path[len] = 0; p->next = NULL; memset(p->parent, 0, sizeof(p->parent[0]) * num_parent); hashcpy(p->oid.hash, q->queue[i]->two->sha1); p->mode = q->queue[i]->two->mode; hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1); p->parent[n].mode = q->queue[i]->one->mode; p->parent[n].status = q->queue[i]->status; *tail = p; tail = &p->next; } return curr; } /* * paths in curr (linked list) and q->queue[] (array) are * both sorted in the tree order. */ i = 0; while ((p = *tail) != NULL) { cmp = ((i >= q->nr) ? -1 : compare_paths(p, q->queue[i]->two)); if (cmp < 0) { /* p->path not in q->queue[]; drop it */ *tail = p->next; free(p); continue; } if (cmp > 0) { /* q->queue[i] not in p->path; skip it */ i++; continue; } hashcpy(p->parent[n].oid.hash, q->queue[i]->one->sha1); p->parent[n].mode = q->queue[i]->one->mode; p->parent[n].status = q->queue[i]->status; tail = &p->next; i++; } return curr; }
/* * If the "reading" argument is set, this function finds out what _object_ * the ref points at by "reading" the ref. The ref, if it is not symbolic, * has to exist, and if it is symbolic, it has to point at an existing ref, * because the "read" goes through the symref to the ref it points at. * * The access that is not "reading" may often be "writing", but does not * have to; it can be merely checking _where it leads to_. If it is a * prelude to "writing" to the ref, a write to a symref that points at * yet-to-be-born ref will create the real ref pointed by the symref. * reading=0 allows the caller to check where such a symref leads to. */ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag) { int depth = MAXDEPTH; ssize_t len; char buffer[256]; static char ref_buffer[256]; if (flag) *flag = 0; for (;;) { char path[PATH_MAX]; struct stat st; char *buf; int fd; if (--depth < 0) return NULL; git_snpath(path, sizeof(path), "%s", ref); /* Special case: non-existing file. */ if (lstat(path, &st) < 0) { struct ref_list *list = get_packed_refs(NULL); while (list) { if (!strcmp(ref, list->name)) { hashcpy(sha1, list->sha1); if (flag) *flag |= REF_ISPACKED; return ref; } list = list->next; } if (reading || errno != ENOENT) return NULL; hashclr(sha1); return ref; } /* Follow "normalized" - ie "refs/.." symlinks by hand */ if (S_ISLNK(st.st_mode)) { len = readlink(path, buffer, sizeof(buffer)-1); if (len >= 5 && !memcmp("refs/", buffer, 5)) { buffer[len] = 0; strcpy(ref_buffer, buffer); ref = ref_buffer; if (flag) *flag |= REF_ISSYMREF; continue; } } /* Is it a directory? */ if (S_ISDIR(st.st_mode)) { errno = EISDIR; return NULL; } /* * Anything else, just open it and try to use it as * a ref */ fd = open(path, O_RDONLY); if (fd < 0) return NULL; len = read_in_full(fd, buffer, sizeof(buffer)-1); close(fd); /* * Is it a symbolic ref? */ if (len < 4 || memcmp("ref:", buffer, 4)) break; buf = buffer + 4; len -= 4; while (len && isspace(*buf)) buf++, len--; while (len && isspace(buf[len-1])) len--; buf[len] = 0; memcpy(ref_buffer, buf, len + 1); ref = ref_buffer; if (flag) *flag |= REF_ISSYMREF; } if (len < 40 || get_sha1_hex(buffer, sha1)) return NULL; return ref; }
int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; int patch_mode = 0, unborn; const char *rev; unsigned char sha1[20]; const char **pathspec = NULL; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, N_("reset HEAD and index"), MIXED), OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT), OPT_SET_INT(0, "hard", &reset_type, N_("reset HEAD, index and working tree"), HARD), OPT_SET_INT(0, "merge", &reset_type, N_("reset HEAD, index and working tree"), MERGE), OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); pathspec = parse_args(argv, prefix, &rev); unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ hashcpy(sha1, EMPTY_TREE_SHA1_BIN); } else if (!pathspec) { struct commit *commit; if (get_sha1_committish(rev, sha1)) die(_("Failed to resolve '%s' as a valid revision."), rev); commit = lookup_commit_reference(sha1); if (!commit) die(_("Could not parse object '%s'."), rev); hashcpy(sha1, commit->object.sha1); } else { struct tree *tree; if (get_sha1_treeish(rev, sha1)) die(_("Failed to resolve '%s' as a valid tree."), rev); tree = parse_tree_indirect(sha1); if (!tree) die(_("Could not parse object '%s'."), rev); hashcpy(sha1, tree->object.sha1); } if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); return run_add_interactive(sha1_to_hex(sha1), "--patch=reset", pathspec); } /* git reset tree [--] paths... can be used to * load chosen paths from the tree into the index without * affecting the working tree nor HEAD. */ if (pathspec) { if (reset_type == MIXED) warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.")); else if (reset_type != NONE) die(_("Cannot do %s reset with paths."), _(reset_type_names[reset_type])); } if (reset_type == NONE) reset_type = MIXED; /* by default */ if (reset_type != SOFT && reset_type != MIXED) setup_work_tree(); if (reset_type == MIXED && is_bare_repository()) die(_("%s reset is not allowed in a bare repository"), _(reset_type_names[reset_type])); /* Soft reset does not touch the index file nor the working tree * at all, but requires them in a good order. Other resets reset * the index file to the tree object we are switching to. */ if (reset_type == SOFT || reset_type == KEEP) die_if_unmerged_cache(reset_type); if (reset_type != SOFT) { struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int newfd = hold_locked_index(lock, 1); if (reset_type == MIXED) { if (read_from_tree(pathspec, sha1)) return 1; } else { int err = reset_index(sha1, reset_type, quiet); if (reset_type == KEEP && !err) err = reset_index(sha1, MIXED, quiet); if (err) die(_("Could not reset index file to revision '%s'."), rev); } if (reset_type == MIXED) { /* Report what has not been updated. */ int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; refresh_index(&the_index, flags, NULL, NULL, _("Unstaged changes after reset:")); } if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock)) die(_("Could not write new index file.")); } if (!pathspec && !unborn) { /* Any resets without paths update HEAD to the head being * switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status = update_refs(rev, sha1); if (reset_type == HARD && !update_ref_status && !quiet) print_new_head_line(lookup_commit_reference(sha1)); } if (!pathspec) remove_branch_state(); return update_ref_status; }
static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, struct int_node *node, unsigned int n) { unsigned char object_sha1[20]; unsigned int prefix_len; void *buf; struct tree_desc desc; struct name_entry entry; int len, path_len; unsigned char type; struct leaf_node *l; buf = fill_tree_descriptor(&desc, subtree->val_sha1); if (!buf) die("Could not read %s for notes-index", sha1_to_hex(subtree->val_sha1)); prefix_len = subtree->key_sha1[19]; assert(prefix_len * 2 >= n); memcpy(object_sha1, subtree->key_sha1, prefix_len); while (tree_entry(&desc, &entry)) { path_len = strlen(entry.path); len = get_sha1_hex_segment(entry.path, path_len, object_sha1 + prefix_len, 20 - prefix_len); if (len < 0) goto handle_non_note; /* entry.path is not a SHA1 */ len += prefix_len; /* * If object SHA1 is complete (len == 20), assume note object * If object SHA1 is incomplete (len < 20), and current * component consists of 2 hex chars, assume note subtree */ if (len <= 20) { type = PTR_TYPE_NOTE; l = (struct leaf_node *) xcalloc(1, sizeof(struct leaf_node)); hashcpy(l->key_sha1, object_sha1); hashcpy(l->val_sha1, entry.oid->hash); if (len < 20) { if (!S_ISDIR(entry.mode) || path_len != 2) goto handle_non_note; /* not subtree */ l->key_sha1[19] = (unsigned char) len; type = PTR_TYPE_SUBTREE; } if (note_tree_insert(t, node, n, l, type, combine_notes_concatenate)) die("Failed to load %s %s into notes tree " "from %s", type == PTR_TYPE_NOTE ? "note" : "subtree", sha1_to_hex(l->key_sha1), t->ref); } continue; handle_non_note: /* * Determine full path for this non-note entry: * The filename is already found in entry.path, but the * directory part of the path must be deduced from the subtree * containing this entry. We assume here that the overall notes * tree follows a strict byte-based progressive fanout * structure (i.e. using 2/38, 2/2/36, etc. fanouts, and not * e.g. 4/36 fanout). This means that if a non-note is found at * path "dead/beef", the following code will register it as * being found on "de/ad/beef". * On the other hand, if you use such non-obvious non-note * paths in the middle of a notes tree, you deserve what's * coming to you ;). Note that for non-notes that are not * SHA1-like at the top level, there will be no problems. * * To conclude, it is strongly advised to make sure non-notes * have at least one non-hex character in the top-level path * component. */ { struct strbuf non_note_path = STRBUF_INIT; const char *q = sha1_to_hex(subtree->key_sha1); int i; for (i = 0; i < prefix_len; i++) { strbuf_addch(&non_note_path, *q++); strbuf_addch(&non_note_path, *q++); strbuf_addch(&non_note_path, '/'); } strbuf_addstr(&non_note_path, entry.path); add_non_note(t, strbuf_detach(&non_note_path, NULL), entry.mode, entry.oid->hash); } } free(buf); }
int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20], const unsigned char a[20], const unsigned char b[20]) { struct commit *commit_base, *commit_a, *commit_b; int parent_count; struct object_array merges; int i; /* store a in result in case we fail */ hashcpy(result, a); /* we can not handle deletion conflicts */ if (is_null_sha1(base)) return 0; if (is_null_sha1(a)) return 0; if (is_null_sha1(b)) return 0; if (add_submodule_odb(path)) { MERGE_WARNING(path, "not checked out"); return 0; } if (!(commit_base = lookup_commit_reference(base)) || !(commit_a = lookup_commit_reference(a)) || !(commit_b = lookup_commit_reference(b))) { MERGE_WARNING(path, "commits not present"); return 0; } /* check whether both changes are forward */ if (!in_merge_bases(commit_base, &commit_a, 1) || !in_merge_bases(commit_base, &commit_b, 1)) { MERGE_WARNING(path, "commits don't follow merge-base"); return 0; } /* Case #1: a is contained in b or vice versa */ if (in_merge_bases(commit_a, &commit_b, 1)) { hashcpy(result, b); return 1; } if (in_merge_bases(commit_b, &commit_a, 1)) { hashcpy(result, a); return 1; } /* * Case #2: There are one or more merges that contain a and b in * the submodule. If there is only one, then present it as a * suggestion to the user, but leave it marked unmerged so the * user needs to confirm the resolution. */ /* find commit which merges them */ parent_count = find_first_merges(&merges, path, commit_a, commit_b); switch (parent_count) { case 0: MERGE_WARNING(path, "merge following commits not found"); break; case 1: MERGE_WARNING(path, "not fast-forward"); fprintf(stderr, "Found a possible merge resolution " "for the submodule:\n"); print_commit((struct commit *) merges.objects[0].item); fprintf(stderr, "If this is correct simply add it to the index " "for example\n" "by using:\n\n" " git update-index --cacheinfo 160000 %s \"%s\"\n\n" "which will accept this suggestion.\n", sha1_to_hex(merges.objects[0].item->sha1), path); break; default: MERGE_WARNING(path, "multiple merges found"); for (i = 0; i < merges.nr; i++) print_commit((struct commit *) merges.objects[i].item); } free(merges.objects); return 0; }
int combine_notes_overwrite(unsigned char *cur_sha1, const unsigned char *new_sha1) { hashcpy(cur_sha1, new_sha1); return 0; }
static int everything_local(struct fetch_pack_args *args, struct ref **refs, struct ref **sought, int nr_sought) { struct ref *ref; int retval; unsigned long cutoff = 0; save_commit_buffer = 0; for (ref = *refs; ref; ref = ref->next) { struct object *o; if (!has_sha1_file(ref->old_sha1)) continue; o = parse_object(ref->old_sha1); if (!o) continue; /* We already have it -- which may mean that we were * in sync with the other side at some time after * that (it is OK if we guess wrong here). */ if (o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; if (!cutoff || cutoff < commit->date) cutoff = commit->date; } } if (!args->depth) { for_each_ref(mark_complete, NULL); for_each_alternate_ref(mark_alternate_complete, NULL); if (cutoff) mark_recent_complete_commits(args, cutoff); } /* * Mark all complete remote refs as common refs. * Don't mark them common yet; the server has to be told so first. */ for (ref = *refs; ref; ref = ref->next) { struct object *o = deref_tag(lookup_object(ref->old_sha1), NULL, 0); if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE)) continue; if (!(o->flags & SEEN)) { rev_list_push((struct commit *)o, COMMON_REF | SEEN); mark_common((struct commit *)o, 1, 1); } } filter_refs(args, refs, sought, nr_sought); for (retval = 1, ref = *refs; ref ; ref = ref->next) { const unsigned char *remote = ref->old_sha1; unsigned char local[20]; struct object *o; o = lookup_object(remote); if (!o || !(o->flags & COMPLETE)) { retval = 0; if (!args->verbose) continue; fprintf(stderr, "want %s (%s)\n", sha1_to_hex(remote), ref->name); continue; } hashcpy(ref->new_sha1, local); if (!args->verbose) continue; fprintf(stderr, "already have %s (%s)\n", sha1_to_hex(remote), ref->name); } return retval; }
static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) { const char *buf = *buffer; unsigned long size = *size_p; const char *cp; char *ep; struct cache_tree *it; int i, subtree_nr; it = NULL; /* skip name, but make sure name exists */ while (size && *buf) { size--; buf++; } if (!size) goto free_return; buf++; size--; it = cache_tree(); cp = buf; it->entry_count = strtol(cp, &ep, 10); if (cp == ep) goto free_return; cp = ep; subtree_nr = strtol(cp, &ep, 10); if (cp == ep) goto free_return; while (size && *buf && *buf != '\n') { size--; buf++; } if (!size) goto free_return; buf++; size--; if (0 <= it->entry_count) { if (size < 20) goto free_return; hashcpy(it->sha1, (const unsigned char*)buf); buf += 20; size -= 20; } #if DEBUG if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n", *buffer, it->entry_count, subtree_nr, sha1_to_hex(it->sha1)); else fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n", *buffer, subtree_nr); #endif /* * Just a heuristic -- we do not add directories that often but * we do not want to have to extend it immediately when we do, * hence +2. */ it->subtree_alloc = subtree_nr + 2; it->down = xcalloc(it->subtree_alloc, sizeof(struct cache_tree_sub *)); for (i = 0; i < subtree_nr; i++) { /* read each subtree */ struct cache_tree *sub; struct cache_tree_sub *subtree; const char *name = buf; sub = read_one(&buf, &size); if (!sub) goto free_return; subtree = cache_tree_sub(it, name); subtree->cache_tree = sub; } if (subtree_nr != it->subtree_nr) die("cache-tree: internal error"); *buffer = buf; *size_p = size; return it; free_return: cache_tree_free(&it); return NULL; }
void create_branch(const char *head, const char *name, const char *start_name, int force, int reflog, enum branch_track track) { struct ref_lock *lock; struct commit *commit; unsigned char sha1[20]; char *real_ref, msg[PATH_MAX + 20]; struct strbuf ref = STRBUF_INIT; int forcing = 0; int len; len = strlen(name); if (interpret_nth_last_branch(name, &ref) != len) { strbuf_reset(&ref); strbuf_add(&ref, name, len); } strbuf_splice(&ref, 0, 0, "refs/heads/", 11); if (check_ref_format(ref.buf)) die("'%s' is not a valid branch name.", name); if (resolve_ref(ref.buf, sha1, 1, NULL)) { if (!force) die("A branch named '%s' already exists.", name); else if (!is_bare_repository() && !strcmp(head, name)) die("Cannot force update the current branch."); forcing = 1; } real_ref = NULL; if (get_sha1(start_name, sha1)) 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 (track == BRANCH_TRACK_EXPLICIT) die("Cannot setup tracking information; starting point is not a branch."); break; case 1: /* Unique completion -- good, only if it is a real ref */ if (track == BRANCH_TRACK_EXPLICIT && !strcmp(real_ref, "HEAD")) die("Cannot setup tracking information; starting point is not a branch."); 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); lock = lock_any_ref_for_update(ref.buf, NULL, 0); if (!lock) die("Failed to lock ref for update: %s.", strerror(errno)); if (reflog) log_all_ref_updates = 1; if (forcing) snprintf(msg, sizeof msg, "branch: Reset from %s", start_name); else snprintf(msg, sizeof msg, "branch: Created from %s", start_name); if (real_ref && track) setup_tracking(name, real_ref, track); if (write_ref_sha1(lock, sha1, msg) < 0) die("Failed to write ref: %s.", strerror(errno)); strbuf_release(&ref); free(real_ref); }
int run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; int silent_on_removed = option & DIFF_SILENT_ON_REMOVED; unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) ? CE_MATCH_RACY_IS_DIRTY : 0); diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/"); if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; entries = active_nr; for (i = 0; i < entries; i++) { struct stat st; unsigned int oldmode, newmode; struct cache_entry *ce = active_cache[i]; int changed; unsigned dirty_submodule = 0; if (DIFF_OPT_TST(&revs->diffopt, QUICK) && DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES)) break; if (!ce_path_match(ce, revs->prune_data)) continue; if (ce_stage(ce)) { struct combine_diff_path *dpath; int num_compare_stages = 0; size_t path_len; path_len = ce_namelen(ce); dpath = xmalloc(combine_diff_path_size(5, path_len)); dpath->path = (char *) &(dpath->parent[5]); dpath->next = NULL; dpath->len = path_len; memcpy(dpath->path, ce->name, path_len); dpath->path[path_len] = '\0'; hashclr(dpath->sha1); memset(&(dpath->parent[0]), 0, sizeof(struct combine_diff_parent)*5); changed = check_removed(ce, &st); if (!changed) dpath->mode = ce_mode_from_stat(ce, st.st_mode); else { if (changed < 0) { perror(ce->name); continue; } if (silent_on_removed) continue; } while (i < entries) { struct cache_entry *nce = active_cache[i]; int stage; if (strcmp(ce->name, nce->name)) break; /* Stage #2 (ours) is the first parent, * stage #3 (theirs) is the second. */ stage = ce_stage(nce); if (2 <= stage) { int mode = nce->ce_mode; num_compare_stages++; hashcpy(dpath->parent[stage-2].sha1, nce->sha1); dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode); dpath->parent[stage-2].status = DIFF_STATUS_MODIFIED; } /* diff against the proper unmerged stage */ if (stage == diff_unmerged_stage) ce = nce; i++; } /* * Compensate for loop update */ i--; if (revs->combine_merges && num_compare_stages == 2) { show_combined_diff(dpath, 2, revs->dense_combined_merges, revs); free(dpath); continue; } free(dpath); dpath = NULL; /* * Show the diff for the 'ce' if we found the one * from the desired stage. */ diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1); if (ce_stage(ce) != diff_unmerged_stage) continue; } if (ce_uptodate(ce) || ce_skip_worktree(ce)) continue; /* If CE_VALID is set, don't look at workdir for file removal */ changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st); if (changed) { if (changed < 0) { perror(ce->name); continue; } if (silent_on_removed) continue; diff_addremove(&revs->diffopt, '-', ce->ce_mode, ce->sha1, ce->name, 0); continue; } changed = match_stat_with_submodule(&revs->diffopt, ce, &st, ce_option, &dirty_submodule); if (!changed && !dirty_submodule) { ce_mark_uptodate(ce); if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) continue; } oldmode = ce->ce_mode; newmode = ce_mode_from_stat(ce, st.st_mode); diff_change(&revs->diffopt, oldmode, newmode, ce->sha1, (changed ? null_sha1 : ce->sha1), ce->name, 0, dirty_submodule); } diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; }
static int do_pick_commit(struct commit *commit, struct replay_opts *opts) { unsigned char head[20]; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL, NULL }; char *defmsg = NULL; struct strbuf msgbuf = STRBUF_INIT; int res, unborn = 0; if (opts->no_commit) { /* * We do not intend to commit immediately. We just want to * merge the differences in, so let's compute the tree * that represents the "current" state for merge-recursive * to work on. */ if (write_cache_as_tree(head, 0, NULL)) die (_("Your index file is unmerged.")); } else { unborn = get_sha1("HEAD", head); if (unborn) hashcpy(head, EMPTY_TREE_SHA1_BIN); if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0)) return error_dirty_index(opts); } discard_cache(); if (!commit->parents) { parent = NULL; } else if (commit->parents->next) { /* Reverting or cherry-picking a merge commit */ int cnt; struct commit_list *p; if (!opts->mainline) return error(_("Commit %s is a merge but no -m option was given."), sha1_to_hex(commit->object.sha1)); for (cnt = 1, p = commit->parents; cnt != opts->mainline && p; cnt++) p = p->next; if (cnt != opts->mainline || !p) return error(_("Commit %s does not have parent %d"), sha1_to_hex(commit->object.sha1), opts->mainline); parent = p->item; } else if (0 < opts->mainline) return error(_("Mainline was specified but commit %s is not a merge."), sha1_to_hex(commit->object.sha1)); else parent = commit->parents->item; if (opts->allow_ff && ((parent && !hashcmp(parent->object.sha1, head)) || (!parent && unborn))) return fast_forward_to(commit->object.sha1, head, unborn); if (parent && parse_commit(parent) < 0) /* TRANSLATORS: The first %s will be "revert" or "cherry-pick", the second %s a SHA1 */ return error(_("%s: cannot parse parent commit %s"), action_name(opts), sha1_to_hex(parent->object.sha1)); if (get_message(commit, &msg) != 0) return error(_("Cannot get commit message for %s"), sha1_to_hex(commit->object.sha1)); /* * "commit" is an existing commit. We would want to apply * the difference it introduces since its first parent "prev" * on top of the current HEAD if we are cherry-pick. Or the * reverse of it if we are revert. */ defmsg = git_pathdup("MERGE_MSG"); if (opts->action == REPLAY_REVERT) { base = commit; base_label = msg.label; next = parent; next_label = msg.parent_label; strbuf_addstr(&msgbuf, "Revert \""); strbuf_addstr(&msgbuf, msg.subject); strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit "); strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); if (commit->parents && commit->parents->next) { strbuf_addstr(&msgbuf, ", reversing\nchanges made to "); strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1)); } strbuf_addstr(&msgbuf, ".\n"); } else { const char *p; base = parent; base_label = msg.parent_label; next = commit; next_label = msg.label; /* * Append the commit log message to msgbuf; it starts * after the tree, parent, author, committer * information followed by "\n\n". */ p = strstr(msg.message, "\n\n"); if (p) { p += 2; strbuf_addstr(&msgbuf, p); } if (opts->record_origin) { if (!has_conforming_footer(&msgbuf, NULL, 0)) strbuf_addch(&msgbuf, '\n'); strbuf_addstr(&msgbuf, cherry_picked_prefix); strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1)); strbuf_addstr(&msgbuf, ")\n"); } } if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) { res = do_recursive_merge(base, next, base_label, next_label, head, &msgbuf, opts); write_message(&msgbuf, defmsg); } else { struct commit_list *common = NULL; struct commit_list *remotes = NULL; write_message(&msgbuf, defmsg); commit_list_insert(base, &common); commit_list_insert(next, &remotes); res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); } /* * If the merge was clean or if it failed due to conflict, we write * CHERRY_PICK_HEAD for the subsequent invocation of commit to use. * However, if the merge did not even start, then we don't want to * write it at all. */ if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1)) write_cherry_pick_head(commit, "CHERRY_PICK_HEAD"); if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1)) write_cherry_pick_head(commit, "REVERT_HEAD"); if (res) { error(opts->action == REPLAY_REVERT ? _("could not revert %s... %s") : _("could not apply %s... %s"), find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), msg.subject); print_advice(res == 1, opts); rerere(opts->allow_rerere_auto); } else { int allow = allow_empty(opts, commit); if (allow < 0) return allow; if (!opts->no_commit) res = run_git_commit(defmsg, opts, allow); } free_message(&msg); free(defmsg); return res; }
/* * new path should be added to combine diff * * 3 cases on how/when it should be called and behaves: * * t, !tp -> path added, all parents lack it * !t, tp -> path removed from all parents * t, tp -> path modified/added * (M for tp[i]=tp[imin], A otherwise) */ static struct combine_diff_path *emit_path(struct combine_diff_path *p, struct strbuf *base, struct diff_options *opt, int nparent, struct tree_desc *t, struct tree_desc *tp, int imin) { unsigned mode; const char *path; const unsigned char *sha1; int pathlen; int old_baselen = base->len; int i, isdir, recurse = 0, emitthis = 1; /* at least something has to be valid */ assert(t || tp); if (t) { /* path present in resulting tree */ sha1 = tree_entry_extract(t, &path, &mode); pathlen = tree_entry_len(&t->entry); isdir = S_ISDIR(mode); } else { /* * a path was removed - take path from imin parent. Also take * mode from that parent, to decide on recursion(1). * * 1) all modes for tp[i]=tp[imin] should be the same wrt * S_ISDIR, thanks to base_name_compare(). */ tree_entry_extract(&tp[imin], &path, &mode); pathlen = tree_entry_len(&tp[imin].entry); isdir = S_ISDIR(mode); sha1 = NULL; mode = 0; } if (DIFF_OPT_TST(opt, RECURSIVE) && isdir) { recurse = 1; emitthis = DIFF_OPT_TST(opt, TREE_IN_RECURSIVE); } if (emitthis) { int keep; struct combine_diff_path *pprev = p; p = path_appendnew(p, nparent, base, path, pathlen, mode, sha1); for (i = 0; i < nparent; ++i) { /* * tp[i] is valid, if present and if tp[i]==tp[imin] - * otherwise, we should ignore it. */ int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ); const unsigned char *sha1_i; unsigned mode_i; p->parent[i].status = !t ? DIFF_STATUS_DELETED : tpi_valid ? DIFF_STATUS_MODIFIED : DIFF_STATUS_ADDED; if (tpi_valid) { sha1_i = tp[i].entry.sha1; mode_i = tp[i].entry.mode; } else { sha1_i = NULL; mode_i = 0; } p->parent[i].mode = mode_i; hashcpy(p->parent[i].oid.hash, sha1_i ? sha1_i : null_sha1); } keep = 1; if (opt->pathchange) keep = opt->pathchange(opt, p); /* * If a path was filtered or consumed - we don't need to add it * to the list and can reuse its memory, leaving it as * pre-allocated element on the tail. * * On the other hand, if path needs to be kept, we need to * correct its .next to NULL, as it was pre-initialized to how * much memory was allocated. * * see path_appendnew() for details. */ if (!keep) p = pprev; else p->next = NULL; } if (recurse) { const unsigned char **parents_sha1; parents_sha1 = xalloca(nparent * sizeof(parents_sha1[0])); for (i = 0; i < nparent; ++i) { /* same rule as in emitthis */ int tpi_valid = tp && !(tp[i].entry.mode & S_IFXMIN_NEQ); parents_sha1[i] = tpi_valid ? tp[i].entry.sha1 : NULL; } strbuf_add(base, path, pathlen); strbuf_addch(base, '/'); p = ll_diff_tree_paths(p, sha1, parents_sha1, nparent, base, opt); xalloca_free(parents_sha1); } strbuf_setlen(base, old_baselen); return p; }