void init_notes(struct notes_tree *t, const char *notes_ref, combine_notes_fn combine_notes, int flags) { unsigned char sha1[20], object_sha1[20]; unsigned mode; struct leaf_node root_tree; if (!t) t = &default_notes_tree; assert(!t->initialized); if (!notes_ref) notes_ref = default_notes_ref(); if (!combine_notes) combine_notes = combine_notes_concatenate; t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node)); t->first_non_note = NULL; t->prev_non_note = NULL; t->ref = xstrdup_or_null(notes_ref); t->combine_notes = combine_notes; t->initialized = 1; t->dirty = 0; if (flags & NOTES_INIT_EMPTY || !notes_ref || read_ref(notes_ref, object_sha1)) return; if (get_tree_entry(object_sha1, "", sha1, &mode)) die("Failed to read notes tree referenced by %s (%s)", notes_ref, sha1_to_hex(object_sha1)); hashclr(root_tree.key_sha1); hashcpy(root_tree.val_sha1, sha1); load_subtree(t, &root_tree, t->root, 0); }
static void diff_tree_local(struct notes_merge_options *o, struct notes_merge_pair *changes, int len, const unsigned char *base, const unsigned char *local) { struct diff_options opt; int i; trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n", len, sha1_to_hex(base), sha1_to_hex(local)); diff_setup(&opt); DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opt) < 0) die("diff_setup_done failed"); diff_tree_sha1(base, local, "", &opt); diffcore_std(&opt); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int match; unsigned char obj[20]; if (verify_notes_filepair(p, obj)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, sha1_to_hex(p->one->sha1), sha1_to_hex(p->two->sha1)); continue; } mp = find_notes_merge_pair_pos(changes, len, obj, 0, &match); if (!match) { trace_printf("\t\tIgnoring local-only change for %s: " "%.7s -> %.7s\n", sha1_to_hex(obj), sha1_to_hex(p->one->sha1), sha1_to_hex(p->two->sha1)); continue; } assert(!hashcmp(mp->obj, obj)); if (is_null_sha1(p->two->sha1)) { /* deletion */ /* * Either this is a true deletion (1), or it is part * of an A/D pair (2), or D/A pair (3): * * (1) mp->local is uninitialized; set it to null_sha1 * (2) mp->local is not uninitialized; don't touch it * (3) mp->local is uninitialized; set it to null_sha1 * (will be overwritten by following addition) */ if (!hashcmp(mp->local, uninitialized)) hashclr(mp->local); } else if (is_null_sha1(p->one->sha1)) { /* addition */ /* * Either this is a true addition (1), or it is part * of an A/D pair (2), or D/A pair (3): * * (1) mp->local is uninitialized; set to p->two->sha1 * (2) mp->local is uninitialized; set to p->two->sha1 * (3) mp->local is null_sha1; set to p->two->sha1 */ assert(is_null_sha1(mp->local) || !hashcmp(mp->local, uninitialized)); hashcpy(mp->local, p->two->sha1); } else { /* modification */ /* * This is a true modification. p->one->sha1 shall * match mp->base, and mp->local shall be uninitialized. * Set mp->local to p->two->sha1. */ assert(!hashcmp(p->one->sha1, mp->base)); assert(!hashcmp(mp->local, uninitialized)); hashcpy(mp->local, p->two->sha1); } trace_printf("\t\tStored local change for %s: %.7s -> %.7s\n", sha1_to_hex(mp->obj), sha1_to_hex(mp->base), sha1_to_hex(mp->local)); } diff_flush(&opt); diff_tree_release_paths(&opt); }
int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; const char **pathspec; char *seen; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, builtin_rm_usage, 0); if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); if (!index_only) setup_work_tree(); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); /* * Drop trailing directory separators from directories so we'll find * submodules in the index. */ for (i = 0; i < argc; i++) { size_t pathlen = strlen(argv[i]); if (pathlen && is_dir_sep(argv[i][pathlen - 1]) && is_directory(argv[i])) { do { pathlen--; } while (pathlen && is_dir_sep(argv[i][pathlen - 1])); argv[i] = xmemdupz(argv[i], pathlen); } } pathspec = get_pathspec(prefix, argv); refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; seen = xcalloc(i, 1); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = ce->name; list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); } if (pathspec) { const char *match; int seen_any = 0; for (i = 0; (match = pathspec[i]) != NULL ; i++) { if (!seen[i]) { if (!ignore_unmatch) { die(_("pathspec '%s' did not match any files"), match); } } else { seen_any = 1; } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die(_("not removing '%s' recursively without -r"), *match ? match : "."); } if (! seen_any) exit(0); } /* * If not forced, the file, the index and the HEAD (if exists) * must match; but the file can already been removed, since * this sequence is a natural "novice" way: * * rm F; git rm F * * Further, if HEAD commit exists, "diff-index --cached" must * report no changes unless forced. */ if (!force) { unsigned char sha1[20]; if (get_sha1("HEAD", sha1)) hashclr(sha1); if (check_local_mod(sha1, index_only)) exit(1); } else if (!index_only) { if (check_submodules_use_gitfiles()) exit(1); } /* * First remove the names from the index: we won't commit * the index unless all of them succeed. */ for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (!quiet) printf("rm '%s'\n", path); if (remove_file_from_cache(path)) die(_("git rm: unable to remove %s"), path); } if (show_only) return 0; /* * Then, unless we used "--cached", remove the filenames from * the workspace. If we fail to remove the first one, we * abort the "git rm" (but once we've successfully removed * any file at all, we'll go ahead and commit to it all: * by then we've already committed ourselves and can't fail * in the middle) */ if (!index_only) { int removed = 0; for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (list.entry[i].is_submodule) { if (is_empty_dir(path)) { if (!rmdir(path)) { removed = 1; continue; } } else { struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; strbuf_release(&buf); continue; } strbuf_release(&buf); /* Fallthrough and let remove_path() fail. */ } } if (!remove_path(path)) { removed = 1; continue; } if (!removed) die_errno("git rm: '%s'", path); } } 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; }
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); } }
int cmd_pull(int argc, const char **argv, const char *prefix) { const char *repo, **refspecs; struct sha1_array merge_heads = SHA1_ARRAY_INIT; unsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ]; unsigned char rebase_fork_point[GIT_SHA1_RAWSZ]; if (!getenv("GIT_REFLOG_ACTION")) set_reflog_message(argc, argv); argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0); parse_repo_refspecs(argc, argv, &repo, &refspecs); if (!opt_ff) opt_ff = xstrdup_or_null(config_get_ff()); if (opt_rebase < 0) opt_rebase = config_get_rebase(); git_config(git_pull_config, NULL); if (read_cache_unmerged()) die_resolve_conflict("Pull"); if (file_exists(git_path("MERGE_HEAD"))) die_conclude_merge(); if (get_sha1("HEAD", orig_head)) hashclr(orig_head); if (!opt_rebase && opt_autostash != -1) die(_("--[no-]autostash option is only valid with --rebase.")); if (opt_rebase) { int autostash = config_autostash; if (opt_autostash != -1) autostash = opt_autostash; if (is_null_sha1(orig_head) && !is_cache_unborn()) die(_("Updating an unborn branch with changes added to the index.")); if (!autostash) die_on_unclean_work_tree(prefix); if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs)) hashclr(rebase_fork_point); } if (run_fetch(repo, refspecs)) return 1; if (opt_dry_run) return 0; if (get_sha1("HEAD", curr_head)) hashclr(curr_head); if (!is_null_sha1(orig_head) && !is_null_sha1(curr_head) && hashcmp(orig_head, curr_head)) { /* * The fetch involved updating the current branch. * * The working tree and the index file are still based on * orig_head commit, but we are merging into curr_head. * Update the working tree to match curr_head. */ warning(_("fetch updated the current branch head.\n" "fast-forwarding your working tree from\n" "commit %s."), sha1_to_hex(orig_head)); if (checkout_fast_forward(orig_head, curr_head, 0)) die(_("Cannot fast-forward your working tree.\n" "After making sure that you saved anything precious from\n" "$ git diff %s\n" "output, run\n" "$ git reset --hard\n" "to recover."), sha1_to_hex(orig_head)); } get_merge_heads(&merge_heads); if (!merge_heads.nr) die_no_merge_candidates(repo, refspecs); if (is_null_sha1(orig_head)) { if (merge_heads.nr > 1) die(_("Cannot merge multiple branches into empty head.")); return pull_into_void(*merge_heads.sha1, curr_head); } else if (opt_rebase) { if (merge_heads.nr > 1) die(_("Cannot rebase onto multiple branches.")); return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point); } else return run_merge(); }
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(&ctx); 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", cgit_version); } }
int cmd_merge(int argc, const char **argv, const char *prefix) { unsigned char result_tree[20]; unsigned char stash[20]; unsigned char head_sha1[20]; struct commit *head_commit; struct strbuf buf = STRBUF_INIT; const char *head_arg; int i, ret = 0, head_subsumed; 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 *remoteheads, *p; void *branch_to_free; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_merge_usage, builtin_merge_options); /* * Check if we are _not_ on a detached HEAD, i.e. if there is a * current branch. */ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL); if (branch && starts_with(branch, "refs/heads/")) branch += 11; if (!branch || is_null_sha1(head_sha1)) head_commit = NULL; else head_commit = lookup_commit_or_die(head_sha1, "HEAD"); init_diff_ui_defaults(); git_config(git_merge_config, NULL); if (branch_mergeoptions) parse_branch_merge_options(branch_mergeoptions); argc = parse_options(argc, argv, prefix, builtin_merge_options, builtin_merge_usage, 0); if (shortlog_len < 0) shortlog_len = (merge_log_config > 0) ? merge_log_config : 0; if (verbosity < 0 && show_progress == -1) show_progress = 0; if (abort_current_merge) { int nargc = 2; const char *nargv[] = {"reset", "--merge", NULL}; if (!file_exists(git_path_merge_head())) die(_("There is no merge to abort (MERGE_HEAD missing).")); /* Invoke 'git reset --merge' */ ret = cmd_reset(nargc, nargv, prefix); goto done; } if (read_cache_unmerged()) die_resolve_conflict("merge"); if (file_exists(git_path_merge_head())) { /* * There is no unmerged entry, don't advise 'git * add/rm <file>', just 'git commit'. */ if (advice_resolve_conflict) die(_("You have not concluded your merge (MERGE_HEAD exists).\n" "Please, commit your changes before you merge.")); else die(_("You have not concluded your merge (MERGE_HEAD exists).")); } if (file_exists(git_path_cherry_pick_head())) { if (advice_resolve_conflict) die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you merge.")); else die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); } resolve_undo_clear(); if (verbosity < 0) show_diffstat = 0; if (squash) { if (fast_forward == FF_NO) die(_("You cannot combine --squash with --no-ff.")); option_commit = 0; } if (!argc) { if (default_to_upstream) argc = setup_with_upstream(&argv); else die(_("No commit specified and merge.defaultToUpstream not set.")); } else if (argc == 1 && !strcmp(argv[0], "-")) { argv[0] = "@{-1}"; } if (!argc) usage_with_options(builtin_merge_usage, builtin_merge_options); if (!head_commit) { /* * 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". */ unsigned char *remote_head_sha1; if (squash) die(_("Squash commit into empty head not supported yet")); if (fast_forward == FF_NO) die(_("Non-fast-forward commit does not make sense into " "an empty head")); remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv, NULL); if (!remoteheads) die(_("%s - not something we can merge"), argv[0]); if (remoteheads->next) die(_("Can merge only exactly one commit into empty head")); remote_head_sha1 = remoteheads->item->object.oid.hash; read_empty(remote_head_sha1, 0); update_ref("initial pull", "HEAD", remote_head_sha1, NULL, 0, UPDATE_REFS_DIE_ON_ERR); goto done; } /* * 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 * commit-ish 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, head_commit->object.oid.hash)) { warning("old-style 'git merge <msg> HEAD <commit>' is deprecated."); strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; argv += 2; argc -= 2; remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv, NULL); } else { /* 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. */ remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv, &merge_msg); } if (!head_commit || !argc) usage_with_options(builtin_merge_usage, builtin_merge_options); if (verify_signatures) { for (p = remoteheads; p; p = p->next) { struct commit *commit = p->item; char hex[GIT_SHA1_HEXSZ + 1]; struct signature_check signature_check; memset(&signature_check, 0, sizeof(signature_check)); check_commit_signature(commit, &signature_check); find_unique_abbrev_r(hex, commit->object.oid.hash, DEFAULT_ABBREV); switch (signature_check.result) { case 'G': break; case 'U': die(_("Commit %s has an untrusted GPG signature, " "allegedly by %s."), hex, signature_check.signer); case 'B': die(_("Commit %s has a bad GPG signature " "allegedly by %s."), hex, signature_check.signer); default: /* 'N' */ die(_("Commit %s does not have a GPG signature."), hex); } if (verbosity >= 0 && signature_check.result == 'G') printf(_("Commit %s has a good GPG signature by %s\n"), hex, signature_check.signer); signature_check_clear(&signature_check); } } strbuf_addstr(&buf, "merge"); for (p = remoteheads; p; p = p->next) strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name); setenv("GIT_REFLOG_ACTION", buf.buf, 0); strbuf_reset(&buf); for (p = remoteheads; p; p = p->next) { struct commit *commit = p->item; strbuf_addf(&buf, "GITHEAD_%s", oid_to_hex(&commit->object.oid)); setenv(buf.buf, merge_remote_util(commit)->name, 1); strbuf_reset(&buf); if (fast_forward != FF_ONLY && merge_remote_util(commit) && merge_remote_util(commit)->obj && merge_remote_util(commit)->obj->type == OBJ_TAG) fast_forward = FF_NO; } if (option_edit < 0) option_edit = default_edit_option(); if (!use_strategies) { if (!remoteheads) ; /* already up-to-date */ else 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) fast_forward = FF_NO; if (use_strategies[i]->attr & NO_TRIVIAL) allow_trivial = 0; } if (!remoteheads) ; /* already up-to-date */ else if (!remoteheads->next) common = get_merge_bases(head_commit, remoteheads->item); else { struct commit_list *list = remoteheads; commit_list_insert(head_commit, &list); common = get_octopus_merge_bases(list); free(list); } update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); if (remoteheads && !common) { /* No common ancestors found. */ if (!allow_unrelated_histories) die(_("refusing to merge unrelated histories")); /* otherwise, we need a real merge. */ } else if (!remoteheads || (!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.")); goto done; } else if (fast_forward != FF_NO && !remoteheads->next && !common->next && !oidcmp(&common->item->object.oid, &head_commit->object.oid)) { /* Again the most common case of merging one remote. */ struct strbuf msg = STRBUF_INIT; struct commit *commit; if (verbosity >= 0) { char from[GIT_SHA1_HEXSZ + 1], to[GIT_SHA1_HEXSZ + 1]; find_unique_abbrev_r(from, head_commit->object.oid.hash, DEFAULT_ABBREV); find_unique_abbrev_r(to, remoteheads->item->object.oid.hash, DEFAULT_ABBREV); printf(_("Updating %s..%s\n"), from, to); } strbuf_addstr(&msg, "Fast-forward"); if (have_message) strbuf_addstr(&msg, " (no commit created; -m option ignored)"); commit = remoteheads->item; if (!commit) { ret = 1; goto done; } if (checkout_fast_forward(head_commit->object.oid.hash, commit->object.oid.hash, overwrite_ignore)) { ret = 1; goto done; } finish(head_commit, remoteheads, commit->object.oid.hash, msg.buf); drop_save(); goto done; } 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 != FF_ONLY) { /* See if it is really trivial. */ git_committer_info(IDENT_STRICT); printf(_("Trying really trivial in-index merge...\n")); if (!read_tree_trivial(common->item->object.oid.hash, head_commit->object.oid.hash, remoteheads->item->object.oid.hash)) { ret = merge_trivial(head_commit, remoteheads); goto done; } 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(head_commit, j->item); if (oidcmp(&common_one->item->object.oid, &j->item->object.oid)) { up_to_date = 0; break; } } if (up_to_date) { finish_up_to_date(_("Already up-to-date. Yeeah!")); goto done; } } if (fast_forward == FF_ONLY) die(_("Not possible to fast-forward, aborting.")); /* We are going to make a new commit. */ git_committer_info(IDENT_STRICT); /* * 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(stash)) hashclr(stash); for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { printf(_("Rewinding the tree to pristine...\n")); restore_state(head_commit->object.oid.hash, stash); } 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, remoteheads, head_commit, 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) { ret = finish_automerge(head_commit, head_subsumed, common, remoteheads, result_tree, wt_strategy); goto done; } /* * Pick the result from the best strategy and have the user fix * it up. */ if (!best_strategy) { restore_state(head_commit->object.oid.hash, stash); 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); ret = 2; goto done; } 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(head_commit->object.oid.hash, stash); printf(_("Using the %s to prepare resolving by hand.\n"), best_strategy); try_merge_strategy(best_strategy, common, remoteheads, head_commit, head_arg); } if (squash) finish(head_commit, remoteheads, NULL, NULL); else write_merge_state(remoteheads); if (merge_was_ok) fprintf(stderr, _("Automatic merge went well; " "stopped before committing as requested\n")); else ret = suggest_conflicts(); done: free(branch_to_free); return ret; }
static struct ref_list *get_ref_dir(const char *submodule, const char *base, struct ref_list *list) { DIR *dir; const char *path; if (submodule) path = git_path_submodule(submodule, "%s", base); else path = git_path("%s", base); dir = opendir(path); if (dir) { struct dirent *de; int baselen = strlen(base); char *ref = xmalloc(baselen + 257); memcpy(ref, base, baselen); if (baselen && base[baselen-1] != '/') ref[baselen++] = '/'; while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; struct stat st; int flag; int namelen; const char *refdir; if (de->d_name[0] == '.') continue; namelen = strlen(de->d_name); if (namelen > 255) continue; if (has_extension(de->d_name, ".lock")) continue; memcpy(ref + baselen, de->d_name, namelen+1); refdir = submodule ? git_path_submodule(submodule, "%s", ref) : git_path("%s", ref); if (stat(refdir, &st) < 0) continue; if (S_ISDIR(st.st_mode)) { list = get_ref_dir(submodule, ref, list); continue; } if (submodule) { hashclr(sha1); flag = 0; if (resolve_gitlink_ref(submodule, ref, sha1) < 0) { hashclr(sha1); flag |= REF_BROKEN; } } else if (!resolve_ref(ref, sha1, 1, &flag)) { hashclr(sha1); flag |= REF_BROKEN; } list = add_ref(ref, sha1, flag, list, NULL); } free(ref); closedir(dir); } return sort_ref_list(list); }
/* This function needs to return a meaningful errno on failure */ static const char *resolve_ref_recursively(struct ref_store *refs, const char *refname, int resolve_flags, unsigned char *sha1, int *flags) { static struct strbuf sb_refname = STRBUF_INIT; int unused_flags; int symref_count; if (!flags) flags = &unused_flags; *flags = 0; if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) || !refname_is_safe(refname)) { errno = EINVAL; return NULL; } /* * dwim_ref() uses REF_ISBROKEN to distinguish between * missing refs and refs that were present but invalid, * to complain about the latter to stderr. * * We don't know whether the ref exists, so don't set * REF_ISBROKEN yet. */ *flags |= REF_BAD_NAME; } for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) { unsigned int read_flags = 0; if (refs->be->read_raw_ref(refs, refname, sha1, &sb_refname, &read_flags)) { *flags |= read_flags; if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING)) return NULL; hashclr(sha1); if (*flags & REF_BAD_NAME) *flags |= REF_ISBROKEN; return refname; } *flags |= read_flags; if (!(read_flags & REF_ISSYMREF)) { if (*flags & REF_BAD_NAME) { hashclr(sha1); *flags |= REF_ISBROKEN; } return refname; } refname = sb_refname.buf; if (resolve_flags & RESOLVE_REF_NO_RECURSE) { hashclr(sha1); return refname; } if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) || !refname_is_safe(refname)) { errno = EINVAL; return NULL; } *flags |= REF_ISBROKEN | REF_BAD_NAME; } } errno = ELOOP; return NULL; }
int create_symref(const char *ref_target, const char *refs_heads_master, const char *logmsg) { const char *lockpath; char ref[1000]; int fd, len, written; char *git_HEAD = git_pathdup("%s", ref_target); unsigned char old_sha1[20], new_sha1[20]; if (logmsg && read_ref(ref_target, old_sha1)) hashclr(old_sha1); if (safe_create_leading_directories(git_HEAD) < 0) return error("unable to create directory for %s", git_HEAD); #ifndef NO_SYMLINK_HEAD if (prefer_symlink_refs) { unlink(git_HEAD); if (!symlink(refs_heads_master, git_HEAD)) goto done; fprintf(stderr, "no symlink - falling back to symbolic ref\n"); } #endif len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master); if (sizeof(ref) <= len) { error("refname too long: %s", refs_heads_master); goto error_free_return; } lockpath = mkpath("%s.lock", git_HEAD); fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); if (fd < 0) { error("Unable to open %s for writing", lockpath); goto error_free_return; } written = write_in_full(fd, ref, len); if (close(fd) != 0 || written != len) { error("Unable to write to %s", lockpath); goto error_unlink_return; } if (rename(lockpath, git_HEAD) < 0) { error("Unable to create %s", git_HEAD); goto error_unlink_return; } if (adjust_shared_perm(git_HEAD)) { error("Unable to fix permissions on %s", lockpath); error_unlink_return: unlink_or_warn(lockpath); error_free_return: free(git_HEAD); return -1; } #ifndef NO_SYMLINK_HEAD done: #endif if (logmsg && !read_ref(refs_heads_master, new_sha1)) log_ref_write(ref_target, old_sha1, new_sha1, logmsg); free(git_HEAD); return 0; }
int cmd_rm(int argc, const char **argv, const char *prefix) { int i; struct pathspec pathspec; char *seen; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, builtin_rm_usage, 0); if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); if (!index_only) setup_work_tree(); hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD | PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, prefix, argv); refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL); seen = xcalloc(pathspec.nr, 1); for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; if (!ce_path_match(ce, &pathspec, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = xstrdup(ce->name); list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); if (list.entry[list.nr++].is_submodule && !is_staging_gitmodules_ok()) die (_("Please stage your changes to .gitmodules or stash them to proceed")); } if (pathspec.nr) { const char *original; int seen_any = 0; for (i = 0; i < pathspec.nr; i++) { original = pathspec.items[i].original; if (!seen[i]) { if (!ignore_unmatch) { die(_("pathspec '%s' did not match any files"), original); } } else { seen_any = 1; } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die(_("not removing '%s' recursively without -r"), *original ? original : "."); } if (!seen_any) exit(0); } /* * If not forced, the file, the index and the HEAD (if exists) * must match; but the file can already been removed, since * this sequence is a natural "novice" way: * * rm F; git rm F * * Further, if HEAD commit exists, "diff-index --cached" must * report no changes unless forced. */ if (!force) { unsigned char sha1[20]; if (get_sha1("HEAD", sha1)) hashclr(sha1); if (check_local_mod(sha1, index_only)) exit(1); } else if (!index_only) { if (check_submodules_use_gitfiles()) exit(1); } /* * First remove the names from the index: we won't commit * the index unless all of them succeed. */ for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (!quiet) printf("rm '%s'\n", path); if (remove_file_from_cache(path)) die(_("git rm: unable to remove %s"), path); } if (show_only) return 0; /* * Then, unless we used "--cached", remove the filenames from * the workspace. If we fail to remove the first one, we * abort the "git rm" (but once we've successfully removed * any file at all, we'll go ahead and commit to it all: * by then we've already committed ourselves and can't fail * in the middle) */ if (!index_only) { int removed = 0, gitmodules_modified = 0; for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (list.entry[i].is_submodule) { if (is_empty_dir(path)) { if (!rmdir(path)) { removed = 1; if (!remove_path_from_gitmodules(path)) gitmodules_modified = 1; continue; } } else { struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; if (!remove_path_from_gitmodules(path)) gitmodules_modified = 1; strbuf_release(&buf); continue; } else if (!file_exists(path)) /* Submodule was removed by user */ if (!remove_path_from_gitmodules(path)) gitmodules_modified = 1; strbuf_release(&buf); /* Fallthrough and let remove_path() fail. */ } } if (!remove_path(path)) { removed = 1; continue; } if (!removed) die_errno("git rm: '%s'", path); } if (gitmodules_modified) stage_updated_gitmodules(); } if (active_cache_changed) { if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); } return 0; }
static int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx, struct strbuf *line_buf) { int patchlen = 0, found_next = 0; int before = -1, after = -1; while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) { char *line = line_buf->buf; char *p = line; int len; if (!memcmp(line, "diff-tree ", 10)) p += 10; else if (!memcmp(line, "commit ", 7)) p += 7; else if (!memcmp(line, "From ", 5)) p += 5; else if (!memcmp(line, "\\ ", 2) && 12 < strlen(line)) continue; if (!get_sha1_hex(p, next_sha1)) { found_next = 1; break; } /* Ignore commit comments */ if (!patchlen && memcmp(line, "diff ", 5)) continue; /* Parsing diff header? */ if (before == -1) { if (!memcmp(line, "index ", 6)) continue; else if (!memcmp(line, "--- ", 4)) before = after = 1; else if (!isalpha(line[0])) break; } /* Looking for a valid hunk header? */ if (before == 0 && after == 0) { if (!memcmp(line, "@@ -", 4)) { /* Parse next hunk, but ignore line numbers. */ scan_hunk_header(line, &before, &after); continue; } /* Split at the end of the patch. */ if (memcmp(line, "diff ", 5)) break; /* Else we're parsing another header. */ before = after = -1; } /* If we get here, we're inside a hunk. */ if (line[0] == '-' || line[0] == ' ') before--; if (line[0] == '+' || line[0] == ' ') after--; /* Compute the sha without whitespace */ len = remove_space(line); patchlen += len; git_SHA1_Update(ctx, line, len); } if (!found_next) hashclr(next_sha1); return patchlen; }
int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; const char **pathspec; char *seen; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, builtin_rm_usage, 0); if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); if (!index_only) setup_work_tree(); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die("index file corrupt"); refresh_cache(REFRESH_QUIET); pathspec = get_pathspec(prefix, argv); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; seen = xcalloc(i, 1); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; add_list(ce->name); } if (pathspec) { const char *match; int seen_any = 0; for (i = 0; (match = pathspec[i]) != NULL ; i++) { if (!seen[i]) { if (!ignore_unmatch) { die("pathspec '%s' did not match any files", match); } } else { seen_any = 1; } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die("not removing '%s' recursively without -r", *match ? match : "."); } if (! seen_any) exit(0); } /* * If not forced, the file, the index and the HEAD (if exists) * must match; but the file can already been removed, since * this sequence is a natural "novice" way: * * rm F; git rm F * * Further, if HEAD commit exists, "diff-index --cached" must * report no changes unless forced. */ if (!force) { unsigned char sha1[20]; if (get_sha1("HEAD", sha1)) hashclr(sha1); if (check_local_mod(sha1, index_only)) exit(1); } /* * First remove the names from the index: we won't commit * the index unless all of them succeed. */ for (i = 0; i < list.nr; i++) { const char *path = list.name[i]; if (!quiet) printf("rm '%s'\n", path); if (remove_file_from_cache(path)) die("git rm: unable to remove %s", path); } if (show_only) return 0; /* * Then, unless we used "--cached", remove the filenames from * the workspace. If we fail to remove the first one, we * abort the "git rm" (but once we've successfully removed * any file at all, we'll go ahead and commit to it all: * by then we've already committed ourselves and can't fail * in the middle) */ if (!index_only) { int removed = 0; for (i = 0; i < list.nr; i++) { const char *path = list.name[i]; if (!remove_path(path)) { removed = 1; continue; } if (!removed) die("git rm: %s: %s", path, strerror(errno)); } } 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 notes_merge(struct notes_merge_options *o, struct notes_tree *local_tree, unsigned char *result_sha1) { unsigned char local_sha1[20], remote_sha1[20]; struct commit *local, *remote; struct commit_list *bases = NULL; const unsigned char *base_sha1, *base_tree_sha1; int result = 0; assert(o->local_ref && o->remote_ref); assert(!strcmp(o->local_ref, local_tree->ref)); hashclr(result_sha1); trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n", o->local_ref, o->remote_ref); /* Dereference o->local_ref into local_sha1 */ if (read_ref_full(o->local_ref, local_sha1, 0, NULL)) die("Failed to resolve local notes ref '%s'", o->local_ref); else if (!check_refname_format(o->local_ref, 0) && is_null_sha1(local_sha1)) local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ else if (!(local = lookup_commit_reference(local_sha1))) die("Could not parse local commit %s (%s)", sha1_to_hex(local_sha1), o->local_ref); trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1)); /* Dereference o->remote_ref into remote_sha1 */ if (get_sha1(o->remote_ref, remote_sha1)) { /* * Failed to get remote_sha1. If o->remote_ref looks like an * unborn ref, perform the merge using an empty notes tree. */ if (!check_refname_format(o->remote_ref, 0)) { hashclr(remote_sha1); remote = NULL; } else { die("Failed to resolve remote notes ref '%s'", o->remote_ref); } } else if (!(remote = lookup_commit_reference(remote_sha1))) { die("Could not parse remote commit %s (%s)", sha1_to_hex(remote_sha1), o->remote_ref); } trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1)); if (!local && !remote) die("Cannot merge empty notes ref (%s) into empty notes ref " "(%s)", o->remote_ref, o->local_ref); if (!local) { /* result == remote commit */ hashcpy(result_sha1, remote_sha1); goto found_result; } if (!remote) { /* result == local commit */ hashcpy(result_sha1, local_sha1); goto found_result; } assert(local && remote); /* Find merge bases */ bases = get_merge_bases(local, remote, 1); if (!bases) { base_sha1 = null_sha1; base_tree_sha1 = EMPTY_TREE_SHA1_BIN; if (o->verbosity >= 4) printf("No merge base found; doing history-less merge\n"); } else if (!bases->next) { base_sha1 = bases->item->object.sha1; base_tree_sha1 = bases->item->tree->object.sha1; if (o->verbosity >= 4) printf("One merge base found (%.7s)\n", sha1_to_hex(base_sha1)); } else { /* TODO: How to handle multiple merge-bases? */ base_sha1 = bases->item->object.sha1; base_tree_sha1 = bases->item->tree->object.sha1; if (o->verbosity >= 3) printf("Multiple merge bases found. Using the first " "(%.7s)\n", sha1_to_hex(base_sha1)); } if (o->verbosity >= 4) printf("Merging remote commit %.7s into local commit %.7s with " "merge-base %.7s\n", sha1_to_hex(remote->object.sha1), sha1_to_hex(local->object.sha1), sha1_to_hex(base_sha1)); if (!hashcmp(remote->object.sha1, base_sha1)) { /* Already merged; result == local commit */ if (o->verbosity >= 2) printf("Already up-to-date!\n"); hashcpy(result_sha1, local->object.sha1); goto found_result; } if (!hashcmp(local->object.sha1, base_sha1)) { /* Fast-forward; result == remote commit */ if (o->verbosity >= 2) printf("Fast-forward\n"); hashcpy(result_sha1, remote->object.sha1); goto found_result; } result = merge_from_diffs(o, base_tree_sha1, local->tree->object.sha1, remote->tree->object.sha1, local_tree); if (result != 0) { /* non-trivial merge (with or without conflicts) */ /* Commit (partial) result */ struct commit_list *parents = NULL; commit_list_insert(remote, &parents); /* LIFO order */ commit_list_insert(local, &parents); create_notes_commit(local_tree, parents, o->commit_msg.buf, result_sha1); } found_result: free_commit_list(bases); strbuf_release(&(o->commit_msg)); trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n", result, sha1_to_hex(result_sha1)); return result; }
/* * 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 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; if (DIFF_OPT_TST(&revs->diffopt, QUIET) && 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)) continue; changed = 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); continue; } changed = ce_match_stat(ce, &st, ce_option); if (!changed) { 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); } diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; }
int cmd_tag(int argc, const char **argv, const char *prefix) { struct strbuf buf = STRBUF_INIT; struct strbuf ref = STRBUF_INIT; unsigned char object[20], prev[20]; const char *object_ref, *tag; struct create_tag_options opt; char *cleanup_arg = NULL; int create_reflog = 0; int annotate = 0, force = 0; int cmdmode = 0, create_tag_object = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; struct ref_filter filter; static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; const char *format = NULL; struct option options[] = { OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"), N_("print <n> lines of each tag message"), PARSE_OPT_OPTARG, NULL, 1 }, OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'), OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'), OPT_GROUP(N_("Tag creation options")), OPT_BOOL('a', "annotate", &annotate, N_("annotated tag, needs a message")), OPT_CALLBACK('m', "message", &msg, N_("message"), N_("tag message"), parse_msg_arg), OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"), N_("how to strip spaces and #comments from message")), OPT_STRING('u', "local-user", &keyid, N_("key-id"), N_("use another key to sign the tag")), OPT__FORCE(&force, N_("replace the tag if exists")), OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")), OPT_GROUP(N_("Tag listing options")), OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")), OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")), OPT_MERGED(&filter, N_("print only tags that are merged")), OPT_NO_MERGED(&filter, N_("print only tags that are not merged")), OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"), N_("field name to sort on"), &parse_opt_ref_sorting), { OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"), N_("print only tags of the object"), 0, parse_opt_object_name }, OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")), OPT_END() }; git_config(git_tag_config, sorting_tail); memset(&opt, 0, sizeof(opt)); memset(&filter, 0, sizeof(filter)); filter.lines = -1; argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0); if (keyid) { opt.sign = 1; set_signing_key(keyid); } create_tag_object = (opt.sign || annotate || msg.given || msgfile); if (argc == 0 && !cmdmode) cmdmode = 'l'; if ((create_tag_object || force) && (cmdmode != 0)) usage_with_options(git_tag_usage, options); finalize_colopts(&colopts, -1); if (cmdmode == 'l' && filter.lines != -1) { if (explicitly_enable_column(colopts)) die(_("--column and -n are incompatible")); colopts = 0; } if (!sorting) sorting = ref_default_sorting(); if (cmdmode == 'l') { int ret; if (column_active(colopts)) { struct column_options copts; memset(&copts, 0, sizeof(copts)); copts.padding = 2; run_column_filter(colopts, &copts); } filter.name_patterns = argv; ret = list_tags(&filter, sorting, format); if (column_active(colopts)) stop_column_filter(); return ret; } if (filter.lines != -1) die(_("-n option is only allowed with -l.")); if (filter.with_commit) die(_("--contains option is only allowed with -l.")); if (filter.points_at.nr) die(_("--points-at option is only allowed with -l.")); if (filter.merge_commit) die(_("--merged and --no-merged option are only allowed with -l")); if (cmdmode == 'd') return for_each_tag_name(argv, delete_tag); if (cmdmode == 'v') return for_each_tag_name(argv, verify_tag); if (msg.given || msgfile) { if (msg.given && msgfile) die(_("only one -F or -m option is allowed.")); if (msg.given) strbuf_addbuf(&buf, &(msg.buf)); else { if (!strcmp(msgfile, "-")) { if (strbuf_read(&buf, 0, 1024) < 0) die_errno(_("cannot read '%s'"), msgfile); } else { if (strbuf_read_file(&buf, msgfile, 1024) < 0) die_errno(_("could not open or read '%s'"), msgfile); } } } tag = argv[0]; object_ref = argc == 2 ? argv[1] : "HEAD"; if (argc > 2) die(_("too many params")); if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); if (strbuf_check_tag_ref(&ref, tag)) die(_("'%s' is not a valid tag name."), tag); if (read_ref(ref.buf, prev)) hashclr(prev); else if (!force) die(_("tag '%s' already exists"), tag); opt.message_given = msg.given || msgfile; if (!cleanup_arg || !strcmp(cleanup_arg, "strip")) opt.cleanup_mode = CLEANUP_ALL; else if (!strcmp(cleanup_arg, "verbatim")) opt.cleanup_mode = CLEANUP_NONE; else if (!strcmp(cleanup_arg, "whitespace")) opt.cleanup_mode = CLEANUP_SPACE; else die(_("Invalid cleanup mode %s"), cleanup_arg); if (create_tag_object) { if (force_sign_annotate && !annotate) opt.sign = 1; create_tag(object, tag, &buf, &opt, prev, object); } transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, ref.buf, object, prev, create_reflog ? REF_FORCE_CREATE_REFLOG : 0, NULL, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); ref_transaction_free(transaction); if (force && !is_null_sha1(prev) && hashcmp(prev, object)) printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV)); strbuf_release(&err); strbuf_release(&buf); strbuf_release(&ref); return 0; }