static int branch_merged(int kind, const char *name, struct commit *rev, struct commit *head_rev) { /* * This checks whether the merge bases of branch and HEAD (or * the other branch this branch builds upon) contains the * branch, which means that the branch has already been merged * safely to HEAD (or the other branch). */ struct commit *reference_rev = NULL; const char *reference_name = NULL; void *reference_name_to_free = NULL; int merged; if (kind == FILTER_REFS_BRANCHES) { struct branch *branch = branch_get(name); const char *upstream = branch_get_upstream(branch, NULL); unsigned char sha1[20]; if (upstream && (reference_name = reference_name_to_free = resolve_refdup(upstream, RESOLVE_REF_READING, sha1, NULL)) != NULL) reference_rev = lookup_commit_reference(sha1); } if (!reference_rev) reference_rev = head_rev; merged = in_merge_bases(rev, reference_rev); /* * After the safety valve is fully redefined to "check with * upstream, if any, otherwise with HEAD", we should just * return the result of the in_merge_bases() above without * any of the following code, but during the transition period, * a gentle reminder is in order. */ if ((head_rev != reference_rev) && in_merge_bases(rev, head_rev) != merged) { if (merged) warning(_("deleting branch '%s' that has been merged to\n" " '%s', but not yet merged to HEAD."), name, reference_name); else warning(_("not deleting branch '%s' that is not yet merged to\n" " '%s', even though it is merged to HEAD."), name, reference_name); } free(reference_name_to_free); return merged; }
/** * Returns remote's upstream branch for the current branch. If remote is NULL, * the current branch's configured default remote is used. Returns NULL if * `remote` does not name a valid remote, HEAD does not point to a branch, * remote is not the branch's configured remote or the branch does not have any * configured upstream branch. */ static const char *get_upstream_branch(const char *remote) { struct remote *rm; struct branch *curr_branch; const char *curr_branch_remote; rm = remote_get(remote); if (!rm) return NULL; curr_branch = branch_get("HEAD"); if (!curr_branch) return NULL; curr_branch_remote = remote_for_branch(curr_branch, NULL); assert(curr_branch_remote); if (strcmp(curr_branch_remote, rm->name)) return NULL; return branch_get_upstream(curr_branch, NULL); }
int cmd_cherry(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct patch_ids ids; struct commit *commit; struct commit_list *list = NULL; struct branch *current_branch; const char *upstream; const char *head = "HEAD"; const char *limit = NULL; int verbose = 0, abbrev = 0; struct option options[] = { OPT__ABBREV(&abbrev), OPT__VERBOSE(&verbose, N_("be verbose")), OPT_END() }; argc = parse_options(argc, argv, prefix, options, cherry_usage, 0); switch (argc) { case 3: limit = argv[2]; /* FALLTHROUGH */ case 2: head = argv[1]; /* FALLTHROUGH */ case 1: upstream = argv[0]; break; default: current_branch = branch_get(NULL); upstream = branch_get_upstream(current_branch, NULL); if (!upstream) { fprintf(stderr, _("Could not find a tracked" " remote branch, please" " specify <upstream> manually.\n")); usage_with_options(cherry_usage, options); } } init_revisions(&revs, prefix); revs.max_parents = 1; if (add_pending_commit(head, &revs, 0)) die(_("Unknown commit %s"), head); if (add_pending_commit(upstream, &revs, UNINTERESTING)) die(_("Unknown commit %s"), upstream); /* Don't say anything if head and upstream are the same. */ if (revs.pending.nr == 2) { struct object_array_entry *o = revs.pending.objects; if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0) return 0; } get_patch_ids(&revs, &ids); if (limit && add_pending_commit(limit, &revs, UNINTERESTING)) die(_("Unknown commit %s"), limit); /* reverse the list of commits */ if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); while ((commit = get_revision(&revs)) != NULL) { commit_list_insert(commit, &list); } while (list) { char sign = '+'; commit = list->item; if (has_commit_patch_id(commit, &ids)) sign = '-'; print_commit(sign, commit, verbose, abbrev); list = list->next; } free_patch_ids(&ids); return 0; }
static struct commit *get_base_commit(const char *base_commit, struct commit **list, int total) { struct commit *base = NULL; struct commit **rev; int i = 0, rev_nr = 0; if (base_commit && strcmp(base_commit, "auto")) { base = lookup_commit_reference_by_name(base_commit); if (!base) die(_("Unknown commit %s"), base_commit); } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) { struct branch *curr_branch = branch_get(NULL); const char *upstream = branch_get_upstream(curr_branch, NULL); if (upstream) { struct commit_list *base_list; struct commit *commit; unsigned char sha1[20]; if (get_sha1(upstream, sha1)) die(_("Failed to resolve '%s' as a valid ref."), upstream); commit = lookup_commit_or_die(sha1, "upstream base"); base_list = get_merge_bases_many(commit, total, list); /* There should be one and only one merge base. */ if (!base_list || base_list->next) die(_("Could not find exact merge base.")); base = base_list->item; free_commit_list(base_list); } else { die(_("Failed to get upstream, if you want to record base commit automatically,\n" "please use git branch --set-upstream-to to track a remote branch.\n" "Or you could specify base commit by --base=<base-commit-id> manually.")); } } ALLOC_ARRAY(rev, total); for (i = 0; i < total; i++) rev[i] = list[i]; rev_nr = total; /* * Get merge base through pair-wise computations * and store it in rev[0]. */ while (rev_nr > 1) { for (i = 0; i < rev_nr / 2; i++) { struct commit_list *merge_base; merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]); if (!merge_base || merge_base->next) die(_("Failed to find exact merge base")); rev[i] = merge_base->item; } if (rev_nr % 2) rev[i] = rev[2 * i]; rev_nr = (rev_nr + 1) / 2; } if (!in_merge_bases(base, rev[0])) die(_("base commit should be the ancestor of revision list")); for (i = 0; i < total; i++) { if (base == list[i]) die(_("base commit shouldn't be in revision list")); } free(rev); return base; }