int write_notes_tree(struct notes_tree *t, struct object_id *result) { struct tree_write_stack root; struct write_each_note_data cb_data; int ret; int flags; if (!t) t = &default_notes_tree; assert(t->initialized); /* Prepare for traversal of current notes tree */ root.next = NULL; /* last forward entry in list is grounded */ strbuf_init(&root.buf, 256 * (32 + GIT_SHA1_HEXSZ)); /* assume 256 entries */ root.path[0] = root.path[1] = '\0'; cb_data.root = &root; cb_data.next_non_note = t->first_non_note; /* Write tree objects representing current notes tree */ flags = FOR_EACH_NOTE_DONT_UNPACK_SUBTREES | FOR_EACH_NOTE_YIELD_SUBTREES; ret = for_each_note(t, flags, write_each_note, &cb_data) || write_each_non_note_until(NULL, &cb_data) || tree_write_stack_finish_subtree(&root) || write_object_file(root.buf.buf, root.buf.len, tree_type, result); strbuf_release(&root.buf); return ret; }
int combine_notes_cat_sort_uniq(struct object_id *cur_oid, const struct object_id *new_oid) { struct string_list sort_uniq_list = STRING_LIST_INIT_DUP; struct strbuf buf = STRBUF_INIT; int ret = 1; /* read both note blob objects into unique_lines */ if (string_list_add_note_lines(&sort_uniq_list, cur_oid)) goto out; if (string_list_add_note_lines(&sort_uniq_list, new_oid)) goto out; string_list_remove_empty_items(&sort_uniq_list, 0); string_list_sort(&sort_uniq_list); string_list_remove_duplicates(&sort_uniq_list, 0); /* create a new blob object from sort_uniq_list */ if (for_each_string_list(&sort_uniq_list, string_list_join_lines_helper, &buf)) goto out; ret = write_object_file(buf.buf, buf.len, blob_type, cur_oid); out: strbuf_release(&buf); string_list_clear(&sort_uniq_list, 0); return ret; }
int cmd_mktag(int argc, const char **argv, const char *prefix) { struct strbuf buf = STRBUF_INIT; struct object_id result; if (argc != 1) usage("git mktag"); git_config(git_default_config, NULL); if (strbuf_read(&buf, 0, 4096) < 0) { die_errno("could not read from stdin"); } /* Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger " */ if (verify_tag(buf.buf, buf.len) < 0) die("invalid tag signature file"); if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0) die("unable to write tag file"); strbuf_release(&buf); printf("%s\n", oid_to_hex(&result)); return 0; }
int notes_cache_put(struct notes_cache *c, struct object_id *key_oid, const char *data, size_t size) { struct object_id value_oid; if (write_object_file(data, size, "blob", &value_oid) < 0) return -1; return add_note(&c->tree, key_oid, &value_oid, NULL); }
static int create_graft(int argc, const char **argv, int force, int gentle) { struct object_id old_oid, new_oid; const char *old_ref = argv[0]; struct commit *commit; struct strbuf buf = STRBUF_INIT; const char *buffer; unsigned long size; if (get_oid(old_ref, &old_oid) < 0) return error(_("Not a valid object name: '%s'"), old_ref); commit = lookup_commit_reference(&old_oid); if (!commit) return error(_("could not parse %s"), old_ref); buffer = get_commit_buffer(commit, &size); strbuf_add(&buf, buffer, size); unuse_commit_buffer(commit, buffer); if (replace_parents(&buf, argc - 1, &argv[1]) < 0) { strbuf_release(&buf); return -1; } if (remove_signature(&buf)) { warning(_("the original commit '%s' has a gpg signature."), old_ref); warning(_("the signature will be removed in the replacement commit!")); } if (check_mergetags(commit, argc, argv)) { strbuf_release(&buf); return -1; } if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) { strbuf_release(&buf); return error(_("could not write replacement commit for: '%s'"), old_ref); } strbuf_release(&buf); if (!oidcmp(&old_oid, &new_oid)) { if (gentle) { warning("graft for '%s' unnecessary", oid_to_hex(&old_oid)); return 0; } return error("new commit is the same as the old one: '%s'", oid_to_hex(&old_oid)); } return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force); }
int combine_notes_concatenate(struct object_id *cur_oid, const struct object_id *new_oid) { char *cur_msg = NULL, *new_msg = NULL, *buf; unsigned long cur_len, new_len, buf_len; enum object_type cur_type, new_type; int ret; /* read in both note blob objects */ if (!is_null_oid(new_oid)) new_msg = read_object_file(new_oid, &new_type, &new_len); if (!new_msg || !new_len || new_type != OBJ_BLOB) { free(new_msg); return 0; } if (!is_null_oid(cur_oid)) cur_msg = read_object_file(cur_oid, &cur_type, &cur_len); if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) { free(cur_msg); free(new_msg); oidcpy(cur_oid, new_oid); return 0; } /* we will separate the notes by two newlines anyway */ if (cur_msg[cur_len - 1] == '\n') cur_len--; /* concatenate cur_msg and new_msg into buf */ buf_len = cur_len + 2 + new_len; buf = (char *) xmalloc(buf_len); memcpy(buf, cur_msg, cur_len); buf[cur_len] = '\n'; buf[cur_len + 1] = '\n'; memcpy(buf + cur_len + 2, new_msg, new_len); free(cur_msg); free(new_msg); /* create a new blob object from buf */ ret = write_object_file(buf, buf_len, blob_type, cur_oid); free(buf); return ret; }
static int tree_write_stack_finish_subtree(struct tree_write_stack *tws) { int ret; struct tree_write_stack *n = tws->next; struct object_id s; if (n) { ret = tree_write_stack_finish_subtree(n); if (ret) return ret; ret = write_object_file(n->buf.buf, n->buf.len, tree_type, &s); if (ret) return ret; strbuf_release(&n->buf); free(n); tws->next = NULL; write_tree_entry(&tws->buf, 040000, tws->path, 2, s.hash); tws->path[0] = tws->path[1] = '\0'; } return 0; }
static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts) { struct cache_entry *ce = active_cache[pos]; const char *path = ce->name; mmfile_t ancestor, ours, theirs; int status; struct object_id oid; mmbuffer_t result_buf; struct object_id threeway[3]; unsigned mode = 0; memset(threeway, 0, sizeof(threeway)); while (pos < active_nr) { int stage; stage = ce_stage(ce); if (!stage || strcmp(path, ce->name)) break; oidcpy(&threeway[stage - 1], &ce->oid); if (stage == 2) mode = create_ce_mode(ce->ce_mode); pos++; ce = active_cache[pos]; } if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2])) return error(_("path '%s' does not have necessary versions"), path); read_mmblob(&ancestor, &threeway[0]); read_mmblob(&ours, &threeway[1]); read_mmblob(&theirs, &threeway[2]); /* * NEEDSWORK: re-create conflicts from merges with * merge.renormalize set, too */ status = ll_merge(&result_buf, path, &ancestor, "base", &ours, "ours", &theirs, "theirs", state->istate, NULL); free(ancestor.ptr); free(ours.ptr); free(theirs.ptr); if (status < 0 || !result_buf.ptr) { free(result_buf.ptr); return error(_("path '%s': cannot merge"), path); } /* * NEEDSWORK: * There is absolutely no reason to write this as a blob object * and create a phony cache entry. This hack is primarily to get * to the write_entry() machinery that massages the contents to * work-tree format and writes out which only allows it for a * cache entry. The code in write_entry() needs to be refactored * to allow us to feed a <buffer, size, mode> instead of a cache * entry. Such a refactoring would help merge_recursive as well * (it also writes the merge result to the object database even * when it may contain conflicts). */ if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid)) die(_("Unable to add merge result for '%s'"), path); free(result_buf.ptr); ce = make_transient_cache_entry(mode, &oid, path, 2); if (!ce) die(_("make_cache_entry failed for path '%s'"), path); status = checkout_entry(ce, state, NULL, nr_checkouts); discard_cache_entry(ce); return status; }
static int update_one(struct cache_tree *it, struct cache_entry **cache, int entries, const char *base, int baselen, int *skip_count, int flags) { struct strbuf buffer; int missing_ok = flags & WRITE_TREE_MISSING_OK; int dryrun = flags & WRITE_TREE_DRY_RUN; int repair = flags & WRITE_TREE_REPAIR; int to_invalidate = 0; int i; assert(!(dryrun && repair)); *skip_count = 0; if (0 <= it->entry_count && has_sha1_file(it->oid.hash)) return it->entry_count; /* * We first scan for subtrees and update them; we start by * marking existing subtrees -- the ones that are unmarked * should not be in the result. */ for (i = 0; i < it->subtree_nr; i++) it->down[i]->used = 0; /* * Find the subtrees and update them. */ i = 0; while (i < entries) { const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, sublen, subcnt, subskip; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (!slash) { i++; continue; } /* * a/bbb/c (base = a/, slash = /c) * ==> * path+baselen = bbb/c, sublen = 3 */ sublen = slash - (path + baselen); sub = find_subtree(it, path + baselen, sublen, 1); if (!sub->cache_tree) sub->cache_tree = cache_tree(); subcnt = update_one(sub->cache_tree, cache + i, entries - i, path, baselen + sublen + 1, &subskip, flags); if (subcnt < 0) return subcnt; if (!subcnt) die("index cache-tree records empty sub-tree"); i += subcnt; sub->count = subcnt; /* to be used in the next loop */ *skip_count += subskip; sub->used = 1; } discard_unused_subtrees(it); /* * Then write out the tree object for this level. */ strbuf_init(&buffer, 8192); i = 0; while (i < entries) { const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub = NULL; const char *path, *slash; int pathlen, entlen; const struct object_id *oid; unsigned mode; int expected_missing = 0; int contains_ita = 0; int ce_missing_ok; path = ce->name; pathlen = ce_namelen(ce); if (pathlen <= baselen || memcmp(base, path, baselen)) break; /* at the end of this level */ slash = strchr(path + baselen, '/'); if (slash) { entlen = slash - (path + baselen); sub = find_subtree(it, path + baselen, entlen, 0); if (!sub) die("cache-tree.c: '%.*s' in '%s' not found", entlen, path + baselen, path); i += sub->count; oid = &sub->cache_tree->oid; mode = S_IFDIR; contains_ita = sub->cache_tree->entry_count < 0; if (contains_ita) { to_invalidate = 1; expected_missing = 1; } } else { oid = &ce->oid; mode = ce->ce_mode; entlen = pathlen - baselen; i++; } ce_missing_ok = mode == S_IFGITLINK || missing_ok || (repository_format_partial_clone && ce_skip_worktree(ce)); if (is_null_oid(oid) || (!ce_missing_ok && !has_object_file(oid))) { strbuf_release(&buffer); if (expected_missing) return -1; return error("invalid object %06o %s for '%.*s'", mode, oid_to_hex(oid), entlen+baselen, path); } /* * CE_REMOVE entries are removed before the index is * written to disk. Skip them to remain consistent * with the future on-disk index. */ if (ce->ce_flags & CE_REMOVE) { *skip_count = *skip_count + 1; continue; } /* * CE_INTENT_TO_ADD entries exist on on-disk index but * they are not part of generated trees. Invalidate up * to root to force cache-tree users to read elsewhere. */ if (!sub && ce_intent_to_add(ce)) { to_invalidate = 1; continue; } /* * "sub" can be an empty tree if all subentries are i-t-a. */ if (contains_ita && is_empty_tree_oid(oid)) continue; strbuf_grow(&buffer, entlen + 100); strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz); #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif } if (repair) { struct object_id oid; hash_object_file(buffer.buf, buffer.len, tree_type, &oid); if (has_object_file(&oid)) oidcpy(&it->oid, &oid); else to_invalidate = 1; } else if (dryrun) { hash_object_file(buffer.buf, buffer.len, tree_type, &it->oid); } else if (write_object_file(buffer.buf, buffer.len, tree_type, &it->oid)) { strbuf_release(&buffer); return -1; } strbuf_release(&buffer); it->entry_count = to_invalidate ? -1 : i - *skip_count; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, oid_to_hex(&it->oid)); #endif return i; }