static char *find_branch_name(struct rev_info *rev) { int i, positive = -1; unsigned char branch_sha1[20]; const unsigned char *tip_sha1; const char *ref; char *full_ref, *branch = NULL; for (i = 0; i < rev->cmdline.nr; i++) { if (rev->cmdline.rev[i].flags & UNINTERESTING) continue; if (positive < 0) positive = i; else return NULL; } if (0 <= positive) { ref = rev->cmdline.rev[positive].name; tip_sha1 = rev->cmdline.rev[positive].item->sha1; } else if (!rev->cmdline.nr && rev->pending.nr == 1 && !strcmp(rev->pending.objects[0].name, "HEAD")) { /* * No actual ref from command line, but "HEAD" from * rev->def was added in setup_revisions() * e.g. format-patch --cover-letter -12 */ ref = "HEAD"; tip_sha1 = rev->pending.objects[0].item->sha1; } else { return NULL; } if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) && !prefixcmp(full_ref, "refs/heads/") && !hashcmp(tip_sha1, branch_sha1)) branch = xstrdup(full_ref + strlen("refs/heads/")); free(full_ref); return branch; }
int compare_file_content (const char *path, SeafStat *st, const unsigned char *ce_sha1, SeafileCrypt *crypt, int repo_version) { CDCFileDescriptor cdc; unsigned char sha1[20]; if (st->st_size == 0) { memset (sha1, 0, 20); } else { memset (&cdc, 0, sizeof(cdc)); cdc.block_sz = calculate_chunk_size (st->st_size); cdc.block_min_sz = cdc.block_sz >> 2; cdc.block_max_sz = cdc.block_sz << 2; cdc.write_block = seafile_write_chunk; if (filename_chunk_cdc (path, &cdc, crypt, FALSE) < 0) { g_warning ("Failed to chunk file.\n"); return -1; } if (repo_version > 0) seaf_fs_manager_calculate_seafile_id_json (repo_version, &cdc, sha1); else memcpy (sha1, cdc.file_sum, 20); if (cdc.blk_sha1s) free (cdc.blk_sha1s); } #if 0 char id1[41], id2[41]; rawdata_to_hex (sha1, id1, 20); rawdata_to_hex (ce_sha1, id2, 20); seaf_debug ("id1: %s, id2: %s.\n", id1, id2); #endif return hashcmp (sha1, ce_sha1); }
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data) { int is_tag = !prefixcmp(path, "refs/tags/"); unsigned char peeled[20]; int is_annotated, prio; /* Reject anything outside refs/tags/ unless --all */ if (!all && !is_tag) return 0; /* Accept only tags that match the pattern, if given */ if (pattern && (!is_tag || fnmatch(pattern, path + 10, 0))) return 0; /* Is it annotated? */ if (!peel_ref(path, peeled)) { is_annotated = !!hashcmp(sha1, peeled); } else { hashcpy(peeled, sha1); is_annotated = 0; } /* * By default, we only use annotated tags, but with --tags * we fall back to lightweight ones (even without --tags, * we still remember lightweight ones, only to give hints * in an error message). --all allows any refs to be used. */ if (is_annotated) prio = 2; else if (is_tag) prio = 1; else prio = 0; add_to_known_names(all ? path + 5 : path + 10, peeled, prio, sha1); return 0; }
static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) { unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step; const unsigned char *p1_base, *p2_base; struct llist_item *p1_hint = NULL, *p2_hint = NULL; const unsigned int hashsz = the_hash_algo->rawsz; p1_base = p1->pack->index_data; p2_base = p2->pack->index_data; p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8); p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8); p1_step = hashsz + ((p1->pack->index_version < 2) ? 4 : 0); p2_step = hashsz + ((p2->pack->index_version < 2) ? 4 : 0); while (p1_off < p1->pack->num_objects * p1_step && p2_off < p2->pack->num_objects * p2_step) { int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off); /* cmp ~ p1 - p2 */ if (cmp == 0) { p1_hint = llist_sorted_remove(p1->unique_objects, (const struct object_id *)(p1_base + p1_off), p1_hint); p2_hint = llist_sorted_remove(p2->unique_objects, (const struct object_id *)(p1_base + p1_off), p2_hint); p1_off += p1_step; p2_off += p2_step; continue; } if (cmp < 0) { /* p1 has the object, p2 doesn't */ p1_off += p1_step; } else { /* p2 has the object, p1 doesn't */ p2_off += p2_step; } } }
/* * To remove a leaf_node: * Search to the tree location appropriate for the given leaf_node's key: * - If location does not hold a matching entry, abort and do nothing. * - Copy the matching entry's value into the given entry. * - Replace the matching leaf_node with a NULL entry (and free the leaf_node). * - Consolidate int_nodes repeatedly, while walking up the tree towards root. */ static void note_tree_remove(struct notes_tree *t, struct int_node *tree, unsigned char n, struct leaf_node *entry) { struct leaf_node *l; struct int_node *parent_stack[20]; unsigned char i, j; void **p = note_tree_search(t, &tree, &n, entry->key_sha1); assert(GET_PTR_TYPE(entry) == 0); /* no type bits set */ if (GET_PTR_TYPE(*p) != PTR_TYPE_NOTE) return; /* type mismatch, nothing to remove */ l = (struct leaf_node *) CLR_PTR_TYPE(*p); if (hashcmp(l->key_sha1, entry->key_sha1)) return; /* key mismatch, nothing to remove */ /* we have found a matching entry */ hashcpy(entry->val_sha1, l->val_sha1); free(l); *p = SET_PTR_TYPE(NULL, PTR_TYPE_NULL); /* consolidate this tree level, and parent levels, if possible */ if (!n) return; /* cannot consolidate top level */ /* first, build stack of ancestors between root and current node */ parent_stack[0] = t->root; for (i = 0; i < n; i++) { j = GET_NIBBLE(i, entry->key_sha1); parent_stack[i + 1] = CLR_PTR_TYPE(parent_stack[i]->a[j]); } assert(i == n && parent_stack[i] == tree); /* next, unwind stack until note_tree_consolidate() is done */ while (i > 0 && !note_tree_consolidate(parent_stack[i], parent_stack[i - 1], GET_NIBBLE(i - 1, entry->key_sha1))) i--; }
static struct commit_list *skip_away(struct commit_list *list, int count) { struct commit_list *cur, *previous; int prn, index, i; prn = get_prn(count); index = (count * prn / PRN_MODULO) * sqrti(prn) / sqrti(PRN_MODULO); cur = list; previous = NULL; for (i = 0; cur; cur = cur->next, i++) { if (i == index) { if (hashcmp(cur->item->object.sha1, current_bad_oid->hash)) return cur; if (previous) return previous; return list; } previous = cur; } return list; }
static void init_skiplist(struct fsck_options *options, const char *path) { static struct sha1_array skiplist = SHA1_ARRAY_INIT; int sorted, fd; char buffer[41]; unsigned char sha1[20]; if (options->skiplist) sorted = options->skiplist->sorted; else { sorted = 1; options->skiplist = &skiplist; } fd = open(path, O_RDONLY); if (fd < 0) die("Could not open skip list: %s", path); for (;;) { int result = read_in_full(fd, buffer, sizeof(buffer)); if (result < 0) die_errno("Could not read '%s'", path); if (!result) break; if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') die("Invalid SHA-1: %s", buffer); sha1_array_append(&skiplist, sha1); if (sorted && skiplist.nr > 1 && hashcmp(skiplist.sha1[skiplist.nr - 2], sha1) > 0) sorted = 0; } close(fd); if (sorted) skiplist.sorted = 1; }
static void stuff_change(struct diff_options *opt, unsigned old_mode, unsigned new_mode, const unsigned char *old_sha1, const unsigned char *new_sha1, int old_sha1_valid, int new_sha1_valid, const char *old_name, const char *new_name) { struct diff_filespec *one, *two; if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) && !hashcmp(old_sha1, new_sha1) && (old_mode == new_mode)) return; if (DIFF_OPT_TST(opt, REVERSE_DIFF)) { unsigned tmp; const unsigned char *tmp_u; const char *tmp_c; tmp = old_mode; old_mode = new_mode; new_mode = tmp; tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u; tmp_c = old_name; old_name = new_name; new_name = tmp_c; } if (opt->prefix && (strncmp(old_name, opt->prefix, opt->prefix_length) || strncmp(new_name, opt->prefix, opt->prefix_length))) return; one = alloc_filespec(old_name); two = alloc_filespec(new_name); fill_filespec(one, old_sha1, old_sha1_valid, old_mode); fill_filespec(two, new_sha1, new_sha1_valid, new_mode); diff_queue(&diff_queued_diff, one, two); }
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 }; struct strbuf msgbuf = STRBUF_INIT; int res, unborn = 0, allow; 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."), oid_to_hex(&commit->object.oid)); 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"), oid_to_hex(&commit->object.oid), opts->mainline); parent = p->item; } else if (0 < opts->mainline) return error(_("Mainline was specified but commit %s is not a merge."), oid_to_hex(&commit->object.oid)); else parent = commit->parents->item; if (opts->allow_ff && ((parent && !hashcmp(parent->object.oid.hash, head)) || (!parent && unborn))) return fast_forward_to(commit->object.oid.hash, head, unborn, opts); 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), oid_to_hex(&parent->object.oid)); if (get_message(commit, &msg) != 0) return error(_("Cannot get commit message for %s"), oid_to_hex(&commit->object.oid)); /* * "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. */ 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, oid_to_hex(&commit->object.oid)); if (commit->parents && commit->parents->next) { strbuf_addstr(&msgbuf, ", reversing\nchanges made to "); strbuf_addstr(&msgbuf, oid_to_hex(&parent->object.oid)); } 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) strbuf_addstr(&msgbuf, skip_blank_lines(p + 2)); if (opts->record_origin) { if (!has_conforming_footer(&msgbuf, NULL, 0)) strbuf_addch(&msgbuf, '\n'); strbuf_addstr(&msgbuf, cherry_picked_prefix); strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid)); 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, git_path_merge_msg()); } else { struct commit_list *common = NULL; struct commit_list *remotes = NULL; write_message(&msgbuf, git_path_merge_msg()); 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)) update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1)) update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); if (res) { error(opts->action == REPLAY_REVERT ? _("could not revert %s... %s") : _("could not apply %s... %s"), find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), msg.subject); print_advice(res == 1, opts); rerere(opts->allow_rerere_auto); goto leave; } allow = allow_empty(opts, commit); if (allow < 0) { res = allow; goto leave; } if (!opts->no_commit) res = run_git_commit(git_path_merge_msg(), opts, allow); leave: free_message(commit, &msg); return res; }
int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(6 * sizeof(const char *)); int i, ref_count = 0; struct strbuf buf = STRBUF_INIT; struct rev_info revs; struct child_process rls; FILE *rls_fout; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *)); argv_boundary[0] = "rev-list"; argv_boundary[1] = "--boundary"; argv_boundary[2] = "--pretty=oneline"; argv_boundary[argc + 2] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_boundary; rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) { unsigned char sha1[20]; if (buf.len > 0 && buf.buf[0] == '-') { write_or_die(bundle_fd, buf.buf, buf.len); if (!get_sha1_hex(buf.buf + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; add_pending_object(&revs, object, xstrdup(buf.buf)); } } else if (!get_sha1_hex(buf.buf, sha1)) { struct object *object = parse_object(sha1); object->flags |= SHOWN; } } strbuf_release(&buf); fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error("unrecognized argument: %s", argv[1]); object_array_remove_duplicates(&revs.pending); for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; char *ref; const char *display_ref; int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) continue; if (read_ref_full(e->name, sha1, 1, &flag)) flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; if (e->item->type == OBJ_TAG && !is_tag_in_date_range(e->item, &revs)) { e->item->flags |= UNINTERESTING; continue; } /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. * * Non commit objects such as tags and blobs do not have * this issue as they are not affected by those extra * constraints. */ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); free(ref); continue; } /* * If you run "git bundle create bndl v1.0..v2.0", the * name of the positive ref is "v2.0" but that is the * commit that is referenced by the tag, and not the tag * itself. */ if (hashcmp(sha1, e->item->sha1)) { /* * Is this the positive end of a range expressed * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ struct commit *one = lookup_commit_reference(sha1); struct object *obj; if (e->item == &(one->object)) { /* * Need to include e->name as an * independent ref to the pack-objects * input, so that the tag is included * in the output; otherwise we would * end up triggering "empty bundle" * error. */ obj = parse_object(sha1); obj->flags |= SHOWN; add_pending_object(&revs, obj, e->name); } free(ref); continue; } ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); free(ref); } if (!ref_count) die ("Refusing to create empty bundle."); /* end header */ write_or_die(bundle_fd, "\n", 1); /* write pack */ argv_pack[0] = "pack-objects"; argv_pack[1] = "--all-progress-implied"; argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; argv_pack[4] = "--delta-base-offset"; argv_pack[5] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_pack; rls.in = -1; rls.out = bundle_fd; rls.git_cmd = 1; if (start_command(&rls)) return error("Could not spawn pack-objects"); /* * start_command closed bundle_fd if it was > 1 * so set the lock fd to -1 so commit_lock_file() * won't fail trying to close it. */ lock.fd = -1; for (i = 0; i < revs.pending.nr; i++) { struct object *object = revs.pending.objects[i].item; if (object->flags & UNINTERESTING) write_or_die(rls.in, "^", 1); write_or_die(rls.in, sha1_to_hex(object->sha1), 40); write_or_die(rls.in, "\n", 1); } close(rls.in); if (finish_command(&rls)) return error ("pack-objects died"); if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno("cannot create '%s'", path); } return 0; }
static void show_patch_diff(struct combine_diff_path *elem, int num_parent, int dense, struct rev_info *rev) { struct diff_options *opt = &rev->diffopt; unsigned long result_size, cnt, lno; char *result, *cp; struct sline *sline; /* survived lines */ int mode_differs = 0; int i, show_hunks; int working_tree_file = is_null_sha1(elem->sha1); int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV; const char *a_prefix, *b_prefix; mmfile_t result_file; context = opt->context; a_prefix = opt->a_prefix ? opt->a_prefix : "a/"; b_prefix = opt->b_prefix ? opt->b_prefix : "b/"; /* Read the result of merge first */ if (!working_tree_file) result = grab_blob(elem->sha1, &result_size); else { /* Used by diff-tree to read from the working tree */ struct stat st; int fd = -1; if (lstat(elem->path, &st) < 0) goto deleted_file; if (S_ISLNK(st.st_mode)) { size_t len = xsize_t(st.st_size); result_size = len; result = xmalloc(len + 1); if (result_size != readlink(elem->path, result, len)) { error("readlink(%s): %s", elem->path, strerror(errno)); return; } result[len] = 0; elem->mode = canon_mode(st.st_mode); } else if (0 <= (fd = open(elem->path, O_RDONLY)) && !fstat(fd, &st)) { size_t len = xsize_t(st.st_size); ssize_t done; int is_file, i; elem->mode = canon_mode(st.st_mode); /* if symlinks don't work, assume symlink if all parents * are symlinks */ is_file = has_symlinks; for (i = 0; !is_file && i < num_parent; i++) is_file = !S_ISLNK(elem->parent[i].mode); if (!is_file) elem->mode = canon_mode(S_IFLNK); result_size = len; result = xmalloc(len + 1); done = read_in_full(fd, result, len); if (done < 0) die("read error '%s'", elem->path); else if (done < len) die("early EOF '%s'", elem->path); result[len] = 0; /* If not a fake symlink, apply filters, e.g. autocrlf */ if (is_file) { struct strbuf buf = STRBUF_INIT; if (convert_to_git(elem->path, result, len, &buf, safe_crlf)) { free(result); result = strbuf_detach(&buf, &len); result_size = len; } } } else { deleted_file: result_size = 0; elem->mode = 0; result = xcalloc(1, 1); } if (0 <= fd) close(fd); } for (cnt = 0, cp = result; cp < result + result_size; cp++) { if (*cp == '\n') cnt++; } if (result_size && result[result_size-1] != '\n') cnt++; /* incomplete line */ sline = xcalloc(cnt+2, sizeof(*sline)); sline[0].bol = result; for (lno = 0; lno <= cnt + 1; lno++) { sline[lno].lost_tail = &sline[lno].lost_head; sline[lno].flag = 0; } for (lno = 0, cp = result; cp < result + result_size; cp++) { if (*cp == '\n') { sline[lno].len = cp - sline[lno].bol; lno++; if (lno < cnt) sline[lno].bol = cp + 1; } } if (result_size && result[result_size-1] != '\n') sline[cnt-1].len = result_size - (sline[cnt-1].bol - result); result_file.ptr = result; result_file.size = result_size; /* Even p_lno[cnt+1] is valid -- that is for the end line number * for deletion hunk at the end. */ sline[0].p_lno = xcalloc((cnt+2) * num_parent, sizeof(unsigned long)); for (lno = 0; lno <= cnt; lno++) sline[lno+1].p_lno = sline[lno].p_lno + num_parent; for (i = 0; i < num_parent; i++) { int j; for (j = 0; j < i; j++) { if (!hashcmp(elem->parent[i].sha1, elem->parent[j].sha1)) { reuse_combine_diff(sline, cnt, i, j); break; } } if (i <= j) combine_diff(elem->parent[i].sha1, &result_file, sline, cnt, i, num_parent); if (elem->parent[i].mode != elem->mode) mode_differs = 1; } show_hunks = make_hunks(sline, cnt, num_parent, dense); if (show_hunks || mode_differs || working_tree_file) { const char *abb; int use_color = DIFF_OPT_TST(opt, COLOR_DIFF); const char *c_meta = diff_get_color(use_color, DIFF_METAINFO); const char *c_reset = diff_get_color(use_color, DIFF_RESET); int added = 0; int deleted = 0; if (rev->loginfo && !rev->no_commit_id) show_log(rev); dump_quoted_path(dense ? "diff --cc " : "diff --combined ", "", elem->path, c_meta, c_reset); printf("%sindex ", c_meta); for (i = 0; i < num_parent; i++) { abb = find_unique_abbrev(elem->parent[i].sha1, abbrev); printf("%s%s", i ? "," : "", abb); } abb = find_unique_abbrev(elem->sha1, abbrev); printf("..%s%s\n", abb, c_reset); if (mode_differs) { deleted = !elem->mode; /* We say it was added if nobody had it */ added = !deleted; for (i = 0; added && i < num_parent; i++) if (elem->parent[i].status != DIFF_STATUS_ADDED) added = 0; if (added) printf("%snew file mode %06o", c_meta, elem->mode); else { if (deleted) printf("%sdeleted file ", c_meta); printf("mode "); for (i = 0; i < num_parent; i++) { printf("%s%06o", i ? "," : "", elem->parent[i].mode); } if (elem->mode) printf("..%06o", elem->mode); } printf("%s\n", c_reset); } if (added) dump_quoted_path("--- ", "", "/dev/null", c_meta, c_reset); else dump_quoted_path("--- ", a_prefix, elem->path, c_meta, c_reset); if (deleted) dump_quoted_path("+++ ", "", "/dev/null", c_meta, c_reset); else dump_quoted_path("+++ ", b_prefix, elem->path, c_meta, c_reset); dump_sline(sline, cnt, num_parent, DIFF_OPT_TST(opt, COLOR_DIFF)); } free(result); for (lno = 0; lno < cnt; lno++) { if (sline[lno].lost_head) { struct lline *ll = sline[lno].lost_head; while (ll) { struct lline *tmp = ll; ll = ll->next; free(tmp); } } } free(sline[0].p_lno); free(sline); }
static const char *update(struct command *cmd) { const char *name = cmd->ref_name; unsigned char *old_sha1 = cmd->old_sha1; unsigned char *new_sha1 = cmd->new_sha1; struct ref_lock *lock; /* only refs/... are allowed */ if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) { error("refusing to create funny ref '%s' remotely", name); return "funny refname"; } if (is_ref_checked_out(name)) { switch (deny_current_branch) { case DENY_IGNORE: break; case DENY_UNCONFIGURED: case DENY_WARN: warning("updating the current branch"); if (deny_current_branch == DENY_UNCONFIGURED) warn_unconfigured_deny(); break; case DENY_REFUSE: error("refusing to update checked out branch: %s", name); return "branch is currently checked out"; } } if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) { error("unpack should have generated %s, " "but I can't find it!", sha1_to_hex(new_sha1)); return "bad pack"; } if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) { if (deny_deletes && !prefixcmp(name, "refs/heads/")) { error("denying ref deletion for %s", name); return "deletion prohibited"; } if (!strcmp(name, head_name)) { switch (deny_delete_current) { case DENY_IGNORE: break; case DENY_WARN: case DENY_UNCONFIGURED: if (deny_delete_current == DENY_UNCONFIGURED) warn_unconfigured_deny_delete_current(); warning("deleting the current branch"); break; case DENY_REFUSE: error("refusing to delete the current branch: %s", name); return "deletion of the current branch prohibited"; } } } if (deny_non_fast_forwards && !is_null_sha1(new_sha1) && !is_null_sha1(old_sha1) && !prefixcmp(name, "refs/heads/")) { struct object *old_object, *new_object; struct commit *old_commit, *new_commit; struct commit_list *bases, *ent; old_object = parse_object(old_sha1); new_object = parse_object(new_sha1); if (!old_object || !new_object || old_object->type != OBJ_COMMIT || new_object->type != OBJ_COMMIT) { error("bad sha1 objects for %s", name); return "bad ref"; } old_commit = (struct commit *)old_object; new_commit = (struct commit *)new_object; bases = get_merge_bases(old_commit, new_commit, 1); for (ent = bases; ent; ent = ent->next) if (!hashcmp(old_sha1, ent->item->object.sha1)) break; free_commit_list(bases); if (!ent) { error("denying non-fast forward %s" " (you should pull first)", name); return "non-fast forward"; } } if (run_update_hook(cmd)) { error("hook declined to update %s", name); return "hook declined"; } if (is_null_sha1(new_sha1)) { if (!parse_object(old_sha1)) { warning ("Allowing deletion of corrupt ref."); old_sha1 = NULL; } if (delete_ref(name, old_sha1, 0)) { error("failed to delete %s", name); return "failed to delete"; } return NULL; /* good */ } else { lock = lock_any_ref_for_update(name, old_sha1, 0); if (!lock) { error("failed to lock %s", name); return "failed to lock"; } if (write_ref_sha1(lock, new_sha1, "push")) { return "failed to write"; /* error() already called */ } return NULL; /* good */ } }
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 flag, 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", head_sha1, 0, &flag); if (branch && !prefixcmp(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"); 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 can 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 can 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 (!allow_fast_forward) die(_("You cannot combine --squash with --no-ff.")); option_commit = 0; } if (!allow_fast_forward && fast_forward_only) die(_("You cannot combine --no-ff with --ff-only.")); if (!abort_current_merge) { 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); /* * This could be traditional "merge <msg> HEAD <commit>..." and * the way we can tell it is to see if the second token is HEAD, * but some people might have misused the interface and used a * committish that is the same as HEAD there instead. * Traditional format never would have "-m" so it is an * additional safety measure to check for it. */ if (!have_message && head_commit && is_old_style_invocation(argc, argv, head_commit->object.sha1)) { strbuf_addstr(&merge_msg, argv[0]); head_arg = argv[1]; argv += 2; argc -= 2; remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); } else if (!head_commit) { struct commit *remote_head; /* * If the merged head is a valid one there is no reason * to forbid "git merge" into a branch yet to be born. * We do the same for "git pull". */ if (argc != 1) die(_("Can merge only exactly one commit into " "empty head")); if (squash) die(_("Squash commit into empty head not supported yet")); if (!allow_fast_forward) die(_("Non-fast-forward commit does not make sense into " "an empty head")); remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); remote_head = remoteheads->item; if (!remote_head) die(_("%s - not something we can merge"), argv[0]); read_empty(remote_head->object.sha1, 0); update_ref("initial pull", "HEAD", remote_head->object.sha1, NULL, 0, DIE_ON_ERR); goto done; } else { struct strbuf merge_names = STRBUF_INIT; /* We are invoked directly as the first-class UI. */ head_arg = "HEAD"; /* * All the rest are the commits being merged; prepare * the standard merge summary message to be appended * to the given message. */ remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv); for (p = remoteheads; p; p = p->next) merge_name(merge_remote_util(p->item)->name, &merge_names); if (!have_message || shortlog_len) { struct fmt_merge_msg_opts opts; memset(&opts, 0, sizeof(opts)); opts.add_title = !have_message; opts.shortlog_len = shortlog_len; fmt_merge_msg(&merge_names, &merge_msg, &opts); if (merge_msg.len) strbuf_setlen(&merge_msg, merge_msg.len - 1); } } if (!head_commit || !argc) usage_with_options(builtin_merge_usage, builtin_merge_options); 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", sha1_to_hex(commit->object.sha1)); setenv(buf.buf, merge_remote_util(commit)->name, 1); strbuf_reset(&buf); if (!fast_forward_only && merge_remote_util(commit) && merge_remote_util(commit)->obj && merge_remote_util(commit)->obj->type == OBJ_TAG) allow_fast_forward = 0; } 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) allow_fast_forward = 0; 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, 1); 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.sha1, NULL, 0, DIE_ON_ERR); if (remoteheads && !common) ; /* No common ancestors found. 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 (allow_fast_forward && !remoteheads->next && !common->next && !hashcmp(common->item->object.sha1, head_commit->object.sha1)) { /* Again the most common case of merging one remote. */ struct strbuf msg = STRBUF_INIT; struct commit *commit; char hex[41]; strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV)); if (verbosity >= 0) printf(_("Updating %s..%s\n"), hex, find_unique_abbrev(remoteheads->item->object.sha1, DEFAULT_ABBREV)); strbuf_addstr(&msg, "Fast-forward"); if (have_message) strbuf_addstr(&msg, " (no commit created; -m option ignored)"); commit = remoteheads->item; if (!commit) { ret = 1; goto done; } if (checkout_fast_forward(head_commit->object.sha1, commit->object.sha1, overwrite_ignore)) { ret = 1; goto done; } finish(head_commit, remoteheads, commit->object.sha1, 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_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.sha1, head_commit->object.sha1, remoteheads->item->object.sha1)) { 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, 1); if (hashcmp(common_one->item->object.sha1, j->item->object.sha1)) { up_to_date = 0; break; } } if (up_to_date) { finish_up_to_date("Already up-to-date. Yeeah!"); goto done; } } if (fast_forward_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)) hashcpy(stash, null_sha1); for (i = 0; i < use_strategies_nr; i++) { int ret; if (i) { printf(_("Rewinding the tree to pristine...\n")); restore_state(head_commit->object.sha1, 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.sha1, 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.sha1, 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(option_renormalize); done: free(branch_to_free); return ret; }
int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) { const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; char *tz_c; int logfd, tz, reccnt = 0; struct stat st; unsigned long date; unsigned char logged_sha1[20]; void *log_mapped; size_t mapsz; logfile = git_path("logs/%s", ref); logfd = open(logfile, O_RDONLY, 0); if (logfd < 0) die_errno("Unable to read log '%s'", logfile); fstat(logfd, &st); if (!st.st_size) die("Log %s is empty.", logfile); mapsz = xsize_t(st.st_size); log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0); logdata = log_mapped; close(logfd); lastrec = NULL; rec = logend = logdata + st.st_size; while (logdata < rec) { reccnt++; if (logdata < rec && *(rec-1) == '\n') rec--; lastgt = NULL; while (logdata < rec && *(rec-1) != '\n') { rec--; if (*rec == '>') lastgt = rec; } if (!lastgt) die("Log %s is corrupt.", logfile); date = strtoul(lastgt + 1, &tz_c, 10); if (date <= at_time || cnt == 0) { tz = strtoul(tz_c, NULL, 10); if (msg) *msg = ref_msg(rec, logend); if (cutoff_time) *cutoff_time = date; if (cutoff_tz) *cutoff_tz = tz; if (cutoff_cnt) *cutoff_cnt = reccnt - 1; if (lastrec) { if (get_sha1_hex(lastrec, logged_sha1)) die("Log %s is corrupt.", logfile); if (get_sha1_hex(rec + 41, sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { warning("Log %s has gap after %s.", logfile, show_date(date, tz, DATE_RFC2822)); } } else if (date == at_time) { if (get_sha1_hex(rec + 41, sha1)) die("Log %s is corrupt.", logfile); } else { if (get_sha1_hex(rec + 41, logged_sha1)) die("Log %s is corrupt.", logfile); if (hashcmp(logged_sha1, sha1)) { warning("Log %s unexpectedly ended on %s.", logfile, show_date(date, tz, DATE_RFC2822)); } } munmap(log_mapped, mapsz); return 0; } lastrec = rec; if (cnt > 0) cnt--; } rec = logdata; while (rec < logend && *rec != '>' && *rec != '\n') rec++; if (rec == logend || *rec == '\n') die("Log %s is corrupt.", logfile); date = strtoul(rec + 1, &tz_c, 10); tz = strtoul(tz_c, NULL, 10); if (get_sha1_hex(logdata, sha1)) die("Log %s is corrupt.", logfile); if (is_null_sha1(sha1)) { if (get_sha1_hex(logdata + 41, sha1)) die("Log %s is corrupt.", logfile); } if (msg) *msg = ref_msg(logdata, logend); munmap(log_mapped, mapsz); if (cutoff_time) *cutoff_time = date; if (cutoff_tz) *cutoff_tz = tz; if (cutoff_cnt) *cutoff_cnt = reccnt; return 1; }
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; }
static int commit_name_cmp(const struct commit_name *cn1, const struct commit_name *cn2, const void *peeled) { return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled); }
static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, const unsigned char *base, const unsigned char *remote, int *num_changes) { struct diff_options opt; struct notes_merge_pair *changes; int i, len = 0; trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n", sha1_to_hex(base), sha1_to_hex(remote)); 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, remote, "", &opt); diffcore_std(&opt); changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair)); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int occupied; 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, 1, &occupied); if (occupied) { /* We've found an addition/deletion pair */ assert(!hashcmp(mp->obj, obj)); if (is_null_sha1(p->one->sha1)) { /* addition */ assert(is_null_sha1(mp->remote)); hashcpy(mp->remote, p->two->sha1); } else if (is_null_sha1(p->two->sha1)) { /* deletion */ assert(is_null_sha1(mp->base)); hashcpy(mp->base, p->one->sha1); } else assert(!"Invalid existing change recorded"); } else { hashcpy(mp->obj, obj); hashcpy(mp->base, p->one->sha1); hashcpy(mp->local, uninitialized); hashcpy(mp->remote, p->two->sha1); len++; } trace_printf("\t\tStored remote change for %s: %.7s -> %.7s\n", sha1_to_hex(mp->obj), sha1_to_hex(mp->base), sha1_to_hex(mp->remote)); } diff_flush(&opt); diff_tree_release_paths(&opt); *num_changes = len; return changes; }
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); if (!current_branch || !current_branch->merge || !current_branch->merge[0] || !current_branch->merge[0]->dst) { fprintf(stderr, _("Could not find a tracked" " remote branch, please" " specify <upstream> manually.\n")); usage_with_options(cherry_usage, options); } upstream = current_branch->merge[0]->dst; } 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 (hashcmp(o[0].item->sha1, o[1].item->sha1) == 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 int verify_clean_subdirectory(const struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { /* * we are about to extract "ce->name"; we would not want to lose * anything in the existing directory there. */ int namelen; int i; struct dir_struct d; char *pathbuf; int cnt = 0; unsigned char sha1[20]; if (S_ISGITLINK(ce->ce_mode) && resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) { /* If we are not going to update the submodule, then * we don't care. */ if (!hashcmp(sha1, ce->sha1)) return 0; return verify_clean_submodule(ce, error_type, o); } /* * First let's make sure we do not have a local modification * in that directory. */ namelen = ce_namelen(ce); for (i = locate_in_src_index(ce, o); i < o->src_index->cache_nr; i++) { struct cache_entry *ce2 = o->src_index->cache[i]; int len = ce_namelen(ce2); if (len < namelen || strncmp(ce->name, ce2->name, namelen) || ce2->name[namelen] != '/') break; /* * ce2->name is an entry in the subdirectory to be * removed. */ if (!ce_stage(ce2)) { if (verify_uptodate(ce2, o)) return -1; add_entry(o, ce2, CE_REMOVE, 0); mark_ce_used(ce2, o); } cnt++; } /* * Then we need to make sure that we do not lose a locally * present file that is not ignored. */ pathbuf = xmalloc(namelen + 2); memcpy(pathbuf, ce->name, namelen); strcpy(pathbuf+namelen, "/"); memset(&d, 0, sizeof(d)); if (o->dir) d.exclude_per_dir = o->dir->exclude_per_dir; i = read_directory(&d, pathbuf, namelen+1, NULL); if (i) return o->gently ? -1 : add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name); free(pathbuf); return cnt; }
static int tipcmp(const void *a_, const void *b_) { const struct tip_table_entry *a = a_, *b = b_; return hashcmp(a->sha1, b->sha1); }
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(); }
/* merge sort the ref list */ static struct ref_list *sort_ref_list(struct ref_list *list) { int psize, qsize, last_merge_count, cmp; struct ref_list *p, *q, *l, *e; struct ref_list *new_list = list; int k = 1; int merge_count = 0; if (!list) return list; do { last_merge_count = merge_count; merge_count = 0; psize = 0; p = new_list; q = new_list; new_list = NULL; l = NULL; while (p) { merge_count++; while (psize < k && q->next) { q = q->next; psize++; } qsize = k; while ((psize > 0) || (qsize > 0 && q)) { if (qsize == 0 || !q) { e = p; p = p->next; psize--; } else if (psize == 0) { e = q; q = q->next; qsize--; } else { cmp = strcmp(q->name, p->name); if (cmp < 0) { e = q; q = q->next; qsize--; } else if (cmp > 0) { e = p; p = p->next; psize--; } else { if (hashcmp(q->sha1, p->sha1)) die("Duplicated ref, and SHA1s don't match: %s", q->name); warning("Duplicated ref: %s", q->name); e = q; q = q->next; qsize--; free(e); e = p; p = p->next; psize--; } } e->next = NULL; if (l) l->next = e; if (!new_list) new_list = e; l = e; } p = q; }; k = k * 2; } while ((last_merge_count != merge_count) || (last_merge_count != 1)); return new_list; }
int send_pack(struct send_pack_args *args, int fd[], struct child_process *conn, struct ref *remote_refs, struct extra_have_objects *extra_have) { int in = fd[0]; int out = fd[1]; struct ref *ref; int new_refs; int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; int ret; /* Does the other end support the reporting? */ if (server_supports("report-status")) ask_for_status_report = 1; if (server_supports("delete-refs")) allow_deleting_refs = 1; if (server_supports("ofs-delta")) args->use_ofs_delta = 1; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" "Perhaps you should specify a branch such as 'master'.\n"); return 0; } /* * Finally, tell the other end! */ new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); else if (!args->send_mirror) continue; ref->deletion = is_null_sha1(ref->new_sha1); if (ref->deletion && !allow_deleting_refs) { ref->status = REF_STATUS_REJECT_NODELETE; continue; } if (!ref->deletion && !hashcmp(ref->old_sha1, ref->new_sha1)) { ref->status = REF_STATUS_UPTODATE; continue; } /* This part determines what can overwrite what. * The rules are: * * (0) you can always use --force or +A:B notation to * selectively force individual ref pairs. * * (1) if the old thing does not exist, it is OK. * * (2) if you do not have the old thing, you are not allowed * to overwrite it; you would not know what you are losing * otherwise. * * (3) if both new and old are commit-ish, and new is a * descendant of old, it is OK. * * (4) regardless of all of the above, removing :B is * always allowed. */ ref->nonfastforward = !ref->deletion && !is_null_sha1(ref->old_sha1) && (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->new_sha1, ref->old_sha1)); if (ref->nonfastforward && !ref->force && !args->force_update) { ref->status = REF_STATUS_REJECT_NONFASTFORWARD; continue; } if (!ref->deletion) new_refs++; if (!args->dry_run) { char *old_hex = sha1_to_hex(ref->old_sha1); char *new_hex = sha1_to_hex(ref->new_sha1); if (ask_for_status_report) { packet_write(out, "%s %s %s%c%s", old_hex, new_hex, ref->name, 0, "report-status"); ask_for_status_report = 0; expect_status_report = 1; } else packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); } ref->status = expect_status_report ? REF_STATUS_EXPECTING_REPORT : REF_STATUS_OK; } packet_flush(out); if (new_refs && !args->dry_run) { if (pack_objects(out, remote_refs, extra_have, args) < 0) { for (ref = remote_refs; ref; ref = ref->next) ref->status = REF_STATUS_NONE; return -1; } } if (expect_status_report) ret = receive_status(in, remote_refs); else ret = 0; if (ret < 0) return ret; for (ref = remote_refs; ref; ref = ref->next) { switch (ref->status) { case REF_STATUS_NONE: case REF_STATUS_UPTODATE: case REF_STATUS_OK: break; default: return -1; } } return 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_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; struct setup_revision_opt s_r_opt; int nr = 0, total, i; int use_stdout = 0; int start_number = -1; int numbered_files = 0; /* _just_ numbers */ int ignore_if_in_upstream = 0; int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; int quiet = 0; char *branch_name = NULL; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, N_("use [PATCH n/m] even with a single patch"), PARSE_OPT_NOARG, numbered_callback }, { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, N_("use [PATCH] even with multiple patches"), PARSE_OPT_NOARG, no_numbered_callback }, OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")), OPT_BOOLEAN(0, "stdout", &use_stdout, N_("print patches to standard out")), OPT_BOOLEAN(0, "cover-letter", &cover_letter, N_("generate a cover letter")), OPT_BOOLEAN(0, "numbered-files", &numbered_files, N_("use simple number sequence for output file names")), OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), N_("use <sfx> instead of '.patch'")), OPT_INTEGER(0, "start-number", &start_number, N_("start numbering patches at <n> instead of 1")), { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"), N_("Use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback }, { OPTION_CALLBACK, 'o', "output-directory", &output_directory, N_("dir"), N_("store resulting files in <dir>"), PARSE_OPT_NONEG, output_directory_callback }, { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, N_("don't strip/add [PATCH]"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, OPT_BOOLEAN(0, "no-binary", &no_binary_diff, N_("don't output binary diffs")), OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, N_("don't include a patch matching a commit upstream")), { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL, N_("show patch format instead of default (patch + stat)"), PARSE_OPT_NONEG | PARSE_OPT_NOARG }, OPT_GROUP(N_("Messaging")), { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"), N_("add email header"), 0, header_callback }, { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"), 0, to_callback }, { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"), 0, cc_callback }, OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"), N_("make first mail a reply to <message-id>")), { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"), N_("attach the patch"), PARSE_OPT_OPTARG, attach_callback }, { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"), N_("inline the patch"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback }, { OPTION_CALLBACK, 0, "thread", &thread, N_("style"), N_("enable message threading, styles: shallow, deep"), PARSE_OPT_OPTARG, thread_callback }, OPT_STRING(0, "signature", &signature, N_("signature"), N_("add a signature")), OPT_BOOLEAN(0, "quiet", &quiet, N_("don't print the patch filenames")), OPT_END() }; extra_hdr.strdup_strings = 1; extra_to.strdup_strings = 1; extra_cc.strdup_strings = 1; git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; rev.max_parents = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; if (default_attach) { rev.mime_boundary = default_attach; rev.no_inline = 1; } /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ argc = parse_options(argc, argv, prefix, builtin_format_patch_options, builtin_format_patch_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); if (do_signoff) { const char *committer; const char *endpos; committer = git_committer_info(IDENT_STRICT); endpos = strchr(committer, '>'); if (!endpos) die(_("bogus committer info %s"), committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } for (i = 0; i < extra_hdr.nr; i++) { strbuf_addstr(&buf, extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); } if (extra_to.nr) strbuf_addstr(&buf, "To: "); for (i = 0; i < extra_to.nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_to.items[i].string); if (i + 1 < extra_to.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } if (extra_cc.nr) strbuf_addstr(&buf, "Cc: "); for (i = 0; i < extra_cc.nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_cc.items[i].string); if (i + 1 < extra_cc.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = strbuf_detach(&buf, NULL); if (start_number < 0) start_number = 1; /* * If numbered is set solely due to format.numbered in config, * and it would conflict with --keep-subject (-k) from the * command line, reset "numbered". */ if (numbered && keep_subject && !numbered_cmdline_opt) numbered = 0; if (numbered && keep_subject) die (_("-n and -k are mutually exclusive.")); if (keep_subject && subject_prefix) die (_("--subject-prefix and -k are mutually exclusive.")); rev.preserve_subject = keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); if (argc > 1) die (_("unrecognized argument: %s"), argv[1]); if (rev.diffopt.output_format & DIFF_FORMAT_NAME) die(_("--name-only does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS) die(_("--name-status does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF) die(_("--check does not make sense")); if (!use_patch_format && (!rev.diffopt.output_format || rev.diffopt.output_format == DIFF_FORMAT_PATCH)) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY; /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (rev.show_notes) init_display_notes(&rev.notes_opt); if (!use_stdout) output_directory = set_outdir(prefix, output_directory); else setup_pager(); if (output_directory) { if (use_stdout) die(_("standard output, or directory, which one?")); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) die_errno(_("Could not create directory '%s'"), output_directory); } if (rev.pending.nr == 1) { if (rev.max_count < 0 && !rev.show_root_diff) { /* * This is traditional behaviour of "git format-patch * origin" that prepares what the origin side still * does not have. */ unsigned char sha1[20]; const char *ref; rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL); if (ref && !prefixcmp(ref, "refs/heads/")) branch_name = xstrdup(ref + strlen("refs/heads/")); else branch_name = xstrdup(""); /* no branch */ } /* * Otherwise, it is "format-patch -22 HEAD", and/or * "format-patch --root HEAD". The user wants * get_revision() to do the usual traversal. */ } /* * We cannot move this anywhere earlier because we do want to * know if --root was given explicitly from the command line. */ rev.show_root_diff = 1; if (cover_letter) { /* * NEEDSWORK:randomly pick one positive commit to show * diffstat; this is often the tip and the command * happens to do the right thing in most cases, but a * complex command like "--cover-letter a b c ^bottom" * picks "c" and shows diffstat between bottom..c * which may not match what the series represents at * all and totally broken. */ int i; for (i = 0; i < rev.pending.nr; i++) { struct object *o = rev.pending.objects[i].item; if (!(o->flags & UNINTERESTING)) head = (struct commit *)o; } /* There is nothing to show; it is not an error, though. */ if (!head) return 0; if (!branch_name) branch_name = find_branch_name(&rev); } if (ignore_if_in_upstream) { /* Don't say anything if head and upstream are the same. */ if (rev.pending.nr == 2) { struct object_array_entry *o = rev.pending.objects; if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } get_patch_ids(&rev, &ids); } if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); if (prepare_revision_walk(&rev)) die(_("revision walk setup failed")); rev.boundary = 1; while ((commit = get_revision(&rev)) != NULL) { if (commit->object.flags & BOUNDARY) { boundary_count++; origin = (boundary_count == 1) ? commit : NULL; continue; } if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) continue; nr++; list = xrealloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; } total = nr; if (!keep_subject && auto_number && total > 1) numbered = 1; if (numbered) rev.total = total + start_number - 1; if (in_reply_to || thread || cover_letter) rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); if (in_reply_to) { const char *msgid = clean_message_id(in_reply_to); string_list_append(rev.ref_message_ids, msgid); } rev.numbered_files = numbered_files; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, use_stdout, numbered, numbered_files, origin, nr, list, head, branch_name, quiet); total++; start_number--; } rev.add_signoff = add_signoff; while (0 <= --nr) { int shown; commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ if (thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* * For deep threading: make every mail * a reply to the previous one, no * matter what other options are set. * * For shallow threading: * * Without --cover-letter and * --in-reply-to, make every mail a * reply to the one before. * * With --in-reply-to but no * --cover-letter, make every mail a * reply to the <reply-to>. * * With --cover-letter, make every * mail but the cover letter a reply * to the cover letter. The cover * letter is a reply to the * --in-reply-to, if specified. */ if (thread == THREAD_SHALLOW && rev.ref_message_ids->nr > 0 && (!cover_letter || rev.nr > 1)) free(rev.message_id); else string_list_append(rev.ref_message_ids, rev.message_id); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet)) die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; /* We put one extra blank line between formatted * patches and this flag is used by log-tree code * to see if it needs to emit a LF before showing * the log; when using one file per patch, we do * not want the extra blank line. */ if (!use_stdout) rev.shown_one = 0; if (shown) { if (rev.mime_boundary) printf("\n--%s%s--\n\n\n", mime_boundary_leader, rev.mime_boundary); else print_signature(); } if (!use_stdout) fclose(stdout); } free(list); free(branch_name); string_list_clear(&extra_to, 0); string_list_clear(&extra_cc, 0); string_list_clear(&extra_hdr, 0); if (ignore_if_in_upstream) free_patch_ids(&ids); return 0; }
int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *logmsg) { static char term = '\n'; struct object *o; if (!lock) return -1; if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) { unlock_ref(lock); return 0; } o = parse_object(sha1); if (!o) { error("Trying to write ref %s with nonexistant object %s", lock->ref_name, sha1_to_hex(sha1)); unlock_ref(lock); return -1; } if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) { error("Trying to write non-commit object %s to branch %s", sha1_to_hex(sha1), lock->ref_name); unlock_ref(lock); return -1; } if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 || write_in_full(lock->lock_fd, &term, 1) != 1 || close_ref(lock) < 0) { error("Couldn't write %s", lock->lk->filename); unlock_ref(lock); return -1; } invalidate_cached_refs(); if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 || (strcmp(lock->ref_name, lock->orig_ref_name) && log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) { unlock_ref(lock); return -1; } if (strcmp(lock->orig_ref_name, "HEAD") != 0) { /* * Special hack: If a branch is updated directly and HEAD * points to it (may happen on the remote side of a push * for example) then logically the HEAD reflog should be * updated too. * A generic solution implies reverse symref information, * but finding all symrefs pointing to the given branch * would be rather costly for this rare event (the direct * update of a branch) to be worth it. So let's cheat and * check with HEAD only which should cover 99% of all usage * scenarios (even 100% of the default ones). */ unsigned char head_sha1[20]; int head_flag; const char *head_ref; head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag); if (head_ref && (head_flag & REF_ISSYMREF) && !strcmp(head_ref, lock->ref_name)) log_ref_write("HEAD", lock->old_sha1, sha1, logmsg); } if (commit_ref(lock)) { error("Couldn't set %s", lock->ref_name); unlock_ref(lock); return -1; } unlock_ref(lock); return 0; }
static int void_hashcmp(const void *a, const void *b) { return hashcmp(a, b); }
static int unresolve_one(const char *path) { int namelen = strlen(path); int pos; int ret = 0; struct cache_entry *ce_2 = NULL, *ce_3 = NULL; /* See if there is such entry in the index. */ pos = cache_name_pos(path, namelen); if (0 <= pos) { /* already merged */ pos = unmerge_cache_entry_at(pos); if (pos < active_nr) { const struct cache_entry *ce = active_cache[pos]; if (ce_stage(ce) && ce_namelen(ce) == namelen && !memcmp(ce->name, path, namelen)) return 0; } /* no resolve-undo information; fall back */ } else { /* If there isn't, either it is unmerged, or * resolved as "removed" by mistake. We do not * want to do anything in the former case. */ pos = -pos-1; if (pos < active_nr) { const struct cache_entry *ce = active_cache[pos]; if (ce_namelen(ce) == namelen && !memcmp(ce->name, path, namelen)) { fprintf(stderr, "%s: skipping still unmerged path.\n", path); goto free_return; } } } /* Grab blobs from given path from HEAD and MERGE_HEAD, * stuff HEAD version in stage #2, * stuff MERGE_HEAD version in stage #3. */ ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); if (!ce_2 || !ce_3) { ret = -1; goto free_return; } if (!hashcmp(ce_2->sha1, ce_3->sha1) && ce_2->ce_mode == ce_3->ce_mode) { fprintf(stderr, "%s: identical in both, skipping.\n", path); goto free_return; } remove_file_from_cache(path); if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { error("%s: cannot add our version to the index.", path); ret = -1; goto free_return; } if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) return 0; error("%s: cannot add their version to the index.", path); ret = -1; free_return: free(ce_2); free(ce_3); return ret; }
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; if (argc > 1 && !strcmp(argv[1], "-v")) { verbose = 1; argc--; argv++; } if (argc > 1 && !strcmp(argv[1], "-h")) usage(cherry_usage); switch (argc) { case 4: limit = argv[3]; /* FALLTHROUGH */ case 3: head = argv[2]; /* FALLTHROUGH */ case 2: upstream = argv[1]; break; default: current_branch = branch_get(NULL); if (!current_branch || !current_branch->merge || !current_branch->merge[0] || !current_branch->merge[0]->dst) { fprintf(stderr, "Could not find a tracked" " remote branch, please" " specify <upstream> manually.\n"); usage(cherry_usage); } upstream = current_branch->merge[0]->dst; } init_revisions(&revs, prefix); revs.diff = 1; revs.combine_merges = 0; revs.ignore_merges = 1; DIFF_OPT_SET(&revs.diffopt, RECURSIVE); 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 (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } get_patch_ids(&revs, &ids, prefix); 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) { /* ignore merges */ if (commit->parents && commit->parents->next) continue; commit_list_insert(commit, &list); } while (list) { char sign = '+'; commit = list->item; if (has_commit_patch_id(commit, &ids)) sign = '-'; if (verbose) { struct strbuf buf = STRBUF_INIT; struct pretty_print_context ctx = {0}; pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, &ctx); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf.buf); strbuf_release(&buf); } else { printf("%c %s\n", sign, sha1_to_hex(commit->object.sha1)); } list = list->next; } free_patch_ids(&ids); return 0; }
static int check_local_mod(unsigned char *head, int index_only) { /* * Items in list are already sorted in the cache order, * so we could do this a lot more efficiently by using * tree_desc based traversal if we wanted to, but I am * lazy, and who cares if removal of files is a tad * slower than the theoretical maximum speed? */ int i, no_head; int errs = 0; no_head = is_null_sha1(head); for (i = 0; i < list.nr; i++) { struct stat st; int pos; struct cache_entry *ce; const char *name = list.entry[i].name; unsigned char sha1[20]; unsigned mode; int local_changes = 0; int staged_changes = 0; pos = cache_name_pos(name, strlen(name)); if (pos < 0) { /* * Skip unmerged entries except for populated submodules * that could lose history when removed. */ pos = get_ours_cache_pos(name, pos); if (pos < 0) continue; if (!S_ISGITLINK(active_cache[pos]->ce_mode) || is_empty_dir(name)) continue; } ce = active_cache[pos]; if (lstat(ce->name, &st) < 0) { if (errno != ENOENT && errno != ENOTDIR) warning("'%s': %s", ce->name, strerror(errno)); /* It already vanished from the working tree */ continue; } else if (S_ISDIR(st.st_mode)) { /* if a file was removed and it is now a * directory, that is the same as ENOENT as * far as git is concerned; we do not track * directories unless they are submodules. */ if (!S_ISGITLINK(ce->ce_mode)) continue; } /* * "rm" of a path that has changes need to be treated * carefully not to allow losing local changes * accidentally. A local change could be (1) file in * work tree is different since the index; and/or (2) * the user staged a content that is different from * the current commit in the index. * * In such a case, you would need to --force the * removal. However, "rm --cached" (remove only from * the index) is safe if the index matches the file in * the work tree or the HEAD commit, as it means that * the content being removed is available elsewhere. */ /* * Is the index different from the file in the work tree? * If it's a submodule, is its work tree modified? */ if (ce_match_stat(ce, &st, 0) || (S_ISGITLINK(ce->ce_mode) && !ok_to_remove_submodule(ce->name))) local_changes = 1; /* * Is the index different from the HEAD commit? By * definition, before the very initial commit, * anything staged in the index is treated by the same * way as changed from the HEAD. */ if (no_head || get_tree_entry(head, name, sha1, &mode) || ce->ce_mode != create_ce_mode(mode) || hashcmp(ce->sha1, sha1)) staged_changes = 1; /* * If the index does not match the file in the work * tree and if it does not match the HEAD commit * either, (1) "git rm" without --cached definitely * will lose information; (2) "git rm --cached" will * lose information unless it is about removing an * "intent to add" entry. */ if (local_changes && staged_changes) { if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD)) errs = error(_("'%s' has staged content different " "from both the file and the HEAD\n" "(use -f to force removal)"), name); } else if (!index_only) { if (staged_changes) errs = error(_("'%s' has changes staged in the index\n" "(use --cached to keep the file, " "or -f to force removal)"), name); if (local_changes) { if (S_ISGITLINK(ce->ce_mode) && !submodule_uses_gitfile(name)) { errs = error(_("submodule '%s' (or one of its nested " "submodules) uses a .git directory\n" "(use 'rm -rf' if you really want to remove " "it including all of its history)"), name); } else errs = error(_("'%s' has local modifications\n" "(use --cached to keep the file, " "or -f to force removal)"), name); } } } return errs; }