static int show_modified(struct rev_info *revs, const struct cache_entry *old_entry, const struct cache_entry *new_entry, int report_missing, int cached, int match_missing) { unsigned int mode, oldmode; const struct object_id *oid; unsigned dirty_submodule = 0; if (get_stat_data(new_entry, &oid, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) { if (report_missing) diff_index_show_file(revs, "-", old_entry, &old_entry->oid, 1, old_entry->ce_mode, 0); return -1; } if (revs->combine_merges && !cached && (!oideq(oid, &old_entry->oid) || !oideq(&old_entry->oid, &new_entry->oid))) { struct combine_diff_path *p; int pathlen = ce_namelen(new_entry); p = xmalloc(combine_diff_path_size(2, pathlen)); p->path = (char *) &p->parent[2]; p->next = NULL; memcpy(p->path, new_entry->name, pathlen); p->path[pathlen] = 0; p->mode = mode; oidclr(&p->oid); memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent)); p->parent[0].status = DIFF_STATUS_MODIFIED; p->parent[0].mode = new_entry->ce_mode; oidcpy(&p->parent[0].oid, &new_entry->oid); p->parent[1].status = DIFF_STATUS_MODIFIED; p->parent[1].mode = old_entry->ce_mode; oidcpy(&p->parent[1].oid, &old_entry->oid); show_combined_diff(p, 2, revs->dense_combined_merges, revs); free(p); return 0; } oldmode = old_entry->ce_mode; if (mode == oldmode && oideq(oid, &old_entry->oid) && !dirty_submodule && !revs->diffopt.flags.find_copies_harder) return 0; diff_change(&revs->diffopt, oldmode, mode, &old_entry->oid, oid, 1, !is_null_oid(oid), old_entry->name, 0, dirty_submodule); return 0; }
int write_index_as_tree(struct object_id *oid, struct index_state *index_state, const char *index_path, int flags, const char *prefix) { int entries, was_valid; struct lock_file lock_file = LOCK_INIT; int ret = 0; hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR); entries = read_index_from(index_state, index_path, get_git_dir()); if (entries < 0) { ret = WRITE_TREE_UNREADABLE_INDEX; goto out; } if (flags & WRITE_TREE_IGNORE_CACHE_TREE) cache_tree_free(&index_state->cache_tree); if (!index_state->cache_tree) index_state->cache_tree = cache_tree(); was_valid = cache_tree_fully_valid(index_state->cache_tree); if (!was_valid) { if (cache_tree_update(index_state, flags) < 0) { ret = WRITE_TREE_UNMERGED_INDEX; goto out; } write_locked_index(index_state, &lock_file, COMMIT_LOCK); /* Not being able to write is fine -- we are only interested * in updating the cache-tree part, and if the next caller * ends up using the old index with unupdated cache-tree part * it misses the work we did here, but that is just a * performance penalty and not a big deal. */ } if (prefix) { struct cache_tree *subtree; subtree = cache_tree_find(index_state->cache_tree, prefix); if (!subtree) { ret = WRITE_TREE_PREFIX_ERROR; goto out; } oidcpy(oid, &subtree->oid); } else oidcpy(oid, &index_state->cache_tree->oid); out: rollback_lock_file(&lock_file); return ret; }
/* * Make a new combine_diff_path from path/mode/sha1 * and append it to paths list tail. * * Memory for created elements could be reused: * * - if last->next == NULL, the memory is allocated; * * - if last->next != NULL, it is assumed that p=last->next was returned * earlier by this function, and p->next was *not* modified. * The memory is then reused from p. * * so for clients, * * - if you do need to keep the element * * p = path_appendnew(p, ...); * process(p); * p->next = NULL; * * - if you don't need to keep the element after processing * * pprev = p; * p = path_appendnew(p, ...); * process(p); * p = pprev; * ; don't forget to free tail->next in the end * * p->parent[] remains uninitialized. */ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last, int nparent, const struct strbuf *base, const char *path, int pathlen, unsigned mode, const struct object_id *oid) { struct combine_diff_path *p; size_t len = st_add(base->len, pathlen); size_t alloclen = combine_diff_path_size(nparent, len); /* if last->next is !NULL - it is a pre-allocated memory, we can reuse */ p = last->next; if (p && (alloclen > (intptr_t)p->next)) { FREE_AND_NULL(p); } if (!p) { p = xmalloc(alloclen); /* * until we go to it next round, .next holds how many bytes we * allocated (for faster realloc - we don't need copying old data). */ p->next = (struct combine_diff_path *)(intptr_t)alloclen; } last->next = p; p->path = (char *)&(p->parent[nparent]); memcpy(p->path, base->buf, base->len); memcpy(p->path + base->len, path, pathlen); p->path[len] = 0; p->mode = mode; oidcpy(&p->oid, oid ? oid : &null_oid); return p; }
static int add_cacheinfo(unsigned int mode, const struct object_id *oid, const char *path, int stage) { int len, option; struct cache_entry *ce; if (!verify_path(path, mode)) return error("Invalid path '%s'", path); len = strlen(path); ce = make_empty_cache_entry(&the_index, len); oidcpy(&ce->oid, oid); memcpy(ce->name, path, len); ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); if (assume_unchanged) ce->ce_flags |= CE_VALID; option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; if (add_cache_entry(ce, option)) return error("%s: cannot add to the index - missing --add option?", path); report("add '%s'", path); return 0; }
/* * Determine whether we can simply reuse the file in the worktree. */ static int use_wt_file(const char *workdir, const char *name, struct object_id *oid) { struct strbuf buf = STRBUF_INIT; struct stat st; int use = 0; strbuf_addstr(&buf, workdir); add_path(&buf, buf.len, name); if (!lstat(buf.buf, &st) && !S_ISLNK(st.st_mode)) { struct object_id wt_oid; int fd = open(buf.buf, O_RDONLY); if (fd >= 0 && !index_fd(wt_oid.hash, fd, &st, OBJ_BLOB, name, 0)) { if (is_null_oid(oid)) { oidcpy(oid, &wt_oid); use = 1; } else if (!oidcmp(oid, &wt_oid)) use = 1; } } strbuf_release(&buf); return use; }
int add_note(struct notes_tree *t, const struct object_id *object_oid, const struct object_id *note_oid, combine_notes_fn combine_notes) { struct leaf_node *l; if (!t) t = &default_notes_tree; assert(t->initialized); t->dirty = 1; if (!combine_notes) combine_notes = t->combine_notes; l = (struct leaf_node *) xmalloc(sizeof(struct leaf_node)); oidcpy(&l->key_oid, object_oid); oidcpy(&l->val_oid, note_oid); return note_tree_insert(t, t->root, 0, l, PTR_TYPE_NOTE, combine_notes); }
/** * Sets merge_base to the octopus merge base of curr_head, merge_head and * fork_point. Returns 0 if a merge base is found, 1 otherwise. */ static int get_octopus_merge_base(struct object_id *merge_base, const struct object_id *curr_head, const struct object_id *merge_head, const struct object_id *fork_point) { struct commit_list *revs = NULL, *result; commit_list_insert(lookup_commit_reference(the_repository, curr_head), &revs); commit_list_insert(lookup_commit_reference(the_repository, merge_head), &revs); if (!is_null_oid(fork_point)) commit_list_insert(lookup_commit_reference(the_repository, fork_point), &revs); result = get_octopus_merge_bases(revs); free_commit_list(revs); reduce_heads_replace(&result); if (!result) return 1; oidcpy(merge_base, &result->item->object.oid); free_commit_list(result); return 0; }
static struct cache_entry *read_one_ent(const char *which, struct object_id *ent, const char *path, int namelen, int stage) { unsigned mode; struct object_id oid; struct cache_entry *ce; if (get_tree_entry(ent, path, &oid, &mode)) { if (which) error("%s: not in %s branch.", path, which); return NULL; } if (mode == S_IFDIR) { if (which) error("%s: not a blob in %s branch.", path, which); return NULL; } ce = make_empty_cache_entry(&the_index, namelen); oidcpy(&ce->oid, &oid); memcpy(ce->name, path, namelen); ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = namelen; ce->ce_mode = create_ce_mode(mode); return ce; }
static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache, const struct object_id *gitmodules_oid, const char *name) { struct submodule *submodule; struct strbuf name_buf = STRBUF_INIT; submodule = cache_lookup_name(cache, gitmodules_oid, name); if (submodule) return submodule; submodule = xmalloc(sizeof(*submodule)); strbuf_addstr(&name_buf, name); submodule->name = strbuf_detach(&name_buf, NULL); submodule->path = NULL; submodule->url = NULL; submodule->update_strategy.type = SM_UPDATE_UNSPECIFIED; submodule->update_strategy.command = NULL; submodule->fetch_recurse = RECURSE_SUBMODULES_NONE; submodule->ignore = NULL; submodule->branch = NULL; submodule->recommend_shallow = -1; oidcpy(&submodule->gitmodules_oid, gitmodules_oid); cache_add(cache, submodule); return submodule; }
static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, const char *name) { struct ref *ref; struct object_id oid; if (!get_oid_hex(name, &oid)) { if (name[GIT_SHA1_HEXSZ] == ' ') { /* <sha1> <ref>, find refname */ name += GIT_SHA1_HEXSZ + 1; } else if (name[GIT_SHA1_HEXSZ] == '\0') { ; /* <sha1>, leave sha1 as name */ } else { /* <ref>, clear cruft from oid */ oidclr(&oid); } } else { /* <ref>, clear cruft from get_oid_hex */ oidclr(&oid); } ref = alloc_ref(name); oidcpy(&ref->old_oid, &oid); (*nr)++; ALLOC_GROW(*sought, *nr, *alloc); (*sought)[*nr - 1] = ref; }
static int process_ref(int len, struct ref ***list, unsigned int flags, struct oid_array *extra_have) { struct object_id old_oid; const char *name; if (parse_oid_hex(packet_buffer, &old_oid, &name)) return 0; if (*name != ' ') return 0; name++; if (extra_have && !strcmp(name, ".have")) { oid_array_append(extra_have, &old_oid); } else if (!strcmp(name, "capabilities^{}")) { die("protocol error: unexpected capabilities^{}"); } else if (check_ref(name, flags)) { struct ref *ref = alloc_ref(name); oidcpy(&ref->old_oid, &old_oid); **list = ref; *list = &ref->next; } check_no_capabilities(len); return 1; }
static void receive_wanted_refs(struct packet_reader *reader, struct ref **sought, int nr_sought) { process_section_header(reader, "wanted-refs", 0); while (packet_reader_read(reader) == PACKET_READ_NORMAL) { struct object_id oid; const char *end; int i; if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ') die(_("expected wanted-ref, got '%s'"), reader->line); for (i = 0; i < nr_sought; i++) { if (!strcmp(end, sought[i]->name)) { oidcpy(&sought[i]->old_oid, &oid); break; } } if (i == nr_sought) die(_("unexpected wanted-ref: '%s'"), reader->line); } if (reader->status != PACKET_READ_DELIM) die(_("error processing wanted refs: %d"), reader->status); }
static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, const char *name) { struct ref *ref; struct object_id oid; const char *p; if (!parse_oid_hex(name, &oid, &p)) { if (*p == ' ') { /* <oid> <ref>, find refname */ name = p + 1; } else if (*p == '\0') { ; /* <oid>, leave oid as name */ } else { /* <ref>, clear cruft from oid */ oidclr(&oid); } } else { /* <ref>, clear cruft from get_oid_hex */ oidclr(&oid); } ref = alloc_ref(name); oidcpy(&ref->old_oid, &oid); (*nr)++; ALLOC_GROW(*sought, *nr, *alloc); (*sought)[*nr - 1] = ref; }
static void prime_cache_tree_rec(struct repository *r, struct cache_tree *it, struct tree *tree) { struct tree_desc desc; struct name_entry entry; int cnt; oidcpy(&it->oid, &tree->object.oid); init_tree_desc(&desc, tree->buffer, tree->size); cnt = 0; while (tree_entry(&desc, &entry)) { if (!S_ISDIR(entry.mode)) cnt++; else { struct cache_tree_sub *sub; struct tree *subtree = lookup_tree(r, entry.oid); if (!subtree->object.parsed) parse_tree(subtree); sub = cache_tree_sub(it, entry.path); sub->cache_tree = cache_tree(); prime_cache_tree_rec(r, sub->cache_tree, subtree); cnt += sub->cache_tree->entry_count; } } it->entry_count = cnt; }
static struct ref *get_refs_from_bundle(struct transport *transport, int for_push, const struct argv_array *ref_prefixes) { struct bundle_transport_data *data = transport->data; struct ref *result = NULL; int i; if (for_push) return NULL; if (data->fd > 0) close(data->fd); data->fd = read_bundle_header(transport->url, &data->header); if (data->fd < 0) die(_("could not read bundle '%s'"), transport->url); for (i = 0; i < data->header.references.nr; i++) { struct ref_list_entry *e = data->header.references.list + i; struct ref *ref = alloc_ref(e->name); oidcpy(&ref->old_oid, &e->oid); ref->next = result; result = ref; } return result; }
static int read_one_reflog(struct object_id *ooid, struct object_id *noid, const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct complete_reflogs *array = cb_data; struct reflog_info *item; ALLOC_GROW(array->items, array->nr + 1, array->alloc); item = array->items + array->nr; oidcpy(&item->ooid, ooid); oidcpy(&item->noid, noid); item->email = xstrdup(email); item->timestamp = timestamp; item->tz = tz; item->message = xstrdup(message); array->nr++; return 0; }
struct ref_entry *create_ref_entry(const char *refname, const struct object_id *oid, int flag) { struct ref_entry *ref; FLEX_ALLOC_STR(ref, name, refname); oidcpy(&ref->u.value.oid, oid); ref->flag = flag; return ref; }
static int iterate_ref_map(void *cb_data, struct object_id *oid) { struct ref **rm = cb_data; struct ref *ref = *rm; if (!ref) return -1; /* end of the list */ *rm = ref->next; oidcpy(oid, &ref->old_oid); return 0; }
static int merge_commit(struct notes_merge_options *o) { struct strbuf msg = STRBUF_INIT; struct object_id oid, parent_oid; struct notes_tree *t; struct commit *partial; struct pretty_print_context pretty_ctx; void *local_ref_to_free; int ret; /* * Read partial merge result from .git/NOTES_MERGE_PARTIAL, * and target notes ref from .git/NOTES_MERGE_REF. */ if (get_oid("NOTES_MERGE_PARTIAL", &oid)) die(_("failed to read ref NOTES_MERGE_PARTIAL")); else if (!(partial = lookup_commit_reference(&oid))) die(_("could not find commit from NOTES_MERGE_PARTIAL.")); else if (parse_commit(partial)) die(_("could not parse commit from NOTES_MERGE_PARTIAL.")); if (partial->parents) oidcpy(&parent_oid, &partial->parents->item->object.oid); else oidclr(&parent_oid); t = xcalloc(1, sizeof(struct notes_tree)); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); o->local_ref = local_ref_to_free = resolve_refdup("NOTES_MERGE_REF", 0, oid.hash, NULL); if (!o->local_ref) die(_("failed to resolve NOTES_MERGE_REF")); if (notes_merge_commit(o, t, partial, &oid)) die(_("failed to finalize notes merge")); /* Reuse existing commit message in reflog message */ memset(&pretty_ctx, 0, sizeof(pretty_ctx)); format_commit_message(partial, "%s", &msg, &pretty_ctx); strbuf_trim(&msg); strbuf_insert(&msg, 0, "notes: ", 7); update_ref(msg.buf, o->local_ref, oid.hash, is_null_oid(&parent_oid) ? NULL : parent_oid.hash, 0, UPDATE_REFS_DIE_ON_ERR); free_notes(t); strbuf_release(&msg); ret = merge_abort(o); free(local_ref_to_free); return ret; }
static void parse_fetch(struct strbuf *buf) { struct ref **to_fetch = NULL; struct ref *list_head = NULL; struct ref **list = &list_head; int alloc_heads = 0, nr_heads = 0; do { const char *p; if (skip_prefix(buf->buf, "fetch ", &p)) { const char *name; struct ref *ref; struct object_id old_oid; if (get_oid_hex(p, &old_oid)) die("protocol error: expected sha/ref, got %s'", p); if (p[GIT_SHA1_HEXSZ] == ' ') name = p + GIT_SHA1_HEXSZ + 1; else if (!p[GIT_SHA1_HEXSZ]) name = ""; else die("protocol error: expected sha/ref, got %s'", p); ref = alloc_ref(name); oidcpy(&ref->old_oid, &old_oid); *list = ref; list = &ref->next; ALLOC_GROW(to_fetch, nr_heads + 1, alloc_heads); to_fetch[nr_heads++] = ref; } else die("http transport does not support %s", buf->buf); strbuf_reset(buf); if (strbuf_getline_lf(buf, stdin) == EOF) return; if (!*buf->buf) break; } while (1); if (fetch(nr_heads, to_fetch)) exit(128); /* error already reported */ free_refs(list_head); free(to_fetch); printf("\n"); fflush(stdout); strbuf_reset(buf); }
static void inspect_filepair(struct diff_filepair *pair) { int binary = 0; unsigned long old_size = 0; unsigned long new_size = 0; if (!show_filepair(pair)) return; files++; lines_added = 0; lines_removed = 0; cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, &new_size, &binary, 0, ctx.qry.ignorews, count_diff_lines); if (files >= slots) { if (slots == 0) slots = 4; else slots = slots * 2; items = xrealloc(items, slots * sizeof(struct fileinfo)); } items[files-1].status = pair->status; oidcpy(items[files-1].old_oid, &pair->one->oid); oidcpy(items[files-1].new_oid, &pair->two->oid); items[files-1].old_mode = pair->one->mode; items[files-1].new_mode = pair->two->mode; items[files-1].old_path = xstrdup(pair->one->path); items[files-1].new_path = xstrdup(pair->two->path); items[files-1].added = lines_added; items[files-1].removed = lines_removed; items[files-1].old_size = old_size; items[files-1].new_size = new_size; items[files-1].binary = binary; if (lines_added + lines_removed > max_changes) max_changes = lines_added + lines_removed; total_adds += lines_added; total_rems += lines_removed; }
static void add_delta_to_list(unsigned nr, const struct object_id *base_oid, off_t base_offset, void *delta, unsigned long size) { struct delta_info *info = xmalloc(sizeof(*info)); oidcpy(&info->base_oid, base_oid); info->base_offset = base_offset; info->size = size; info->delta = delta; info->nr = nr; info->next = delta_list; delta_list = info; }
static void generate_id_list(int stable) { struct object_id oid, n, result; int patchlen; struct strbuf line_buf = STRBUF_INIT; oidclr(&oid); while (!feof(stdin)) { patchlen = get_one_patchid(&n, &result, &line_buf, stable); flush_current_id(patchlen, &oid, &result); oidcpy(&oid, &n); } strbuf_release(&line_buf); }
static int walk_tree(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; if (walk_tree_ctx->file_only && !S_ISREG(mode)) return READ_TREE_RECURSIVE; if (strncmp(base->buf, walk_tree_ctx->match_path, base->len) || strcmp(walk_tree_ctx->match_path + base->len, pathname)) return READ_TREE_RECURSIVE; oidcpy(walk_tree_ctx->matched_oid, oid); walk_tree_ctx->found_path = 1; return 0; }
static void prepare_bases(struct base_tree_info *bases, struct commit *base, struct commit **list, int total) { struct commit *commit; struct rev_info revs; struct diff_options diffopt; int i; if (!base) return; diff_setup(&diffopt); DIFF_OPT_SET(&diffopt, RECURSIVE); diff_setup_done(&diffopt); oidcpy(&bases->base_commit, &base->object.oid); init_revisions(&revs, NULL); revs.max_parents = 1; revs.topo_order = 1; for (i = 0; i < total; i++) { list[i]->object.flags &= ~UNINTERESTING; add_pending_object(&revs, &list[i]->object, "rev_list"); list[i]->util = (void *)1; } base->object.flags |= UNINTERESTING; add_pending_object(&revs, &base->object, "base"); if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); /* * Traverse the commits list, get prerequisite patch ids * and stuff them in bases structure. */ while ((commit = get_revision(&revs)) != NULL) { unsigned char sha1[20]; struct object_id *patch_id; if (commit->util) continue; if (commit_patch_id(commit, &diffopt, sha1)) die(_("cannot get patch id")); ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id); patch_id = bases->patch_id + bases->nr_patch_id; hashcpy(patch_id->hash, sha1); bases->nr_patch_id++; } }
static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator, struct object_id *peeled) { struct packed_ref_iterator *iter = (struct packed_ref_iterator *)ref_iterator; if ((iter->base.flags & REF_KNOWS_PEELED)) { oidcpy(peeled, &iter->peeled); return is_null_oid(&iter->peeled) ? -1 : 0; } else if ((iter->base.flags & (REF_ISBROKEN | REF_ISSYMREF))) { return -1; } else { return !!peel_object(&iter->oid, peeled); } }
void fetch_objects(const char *remote_name, const struct object_id *oids, int oid_nr) { struct ref *ref = NULL; int i; for (i = 0; i < oid_nr; i++) { struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i])); oidcpy(&new_ref->old_oid, &oids[i]); new_ref->exact_oid = 1; new_ref->next = ref; ref = new_ref; } fetch_refs(remote_name, ref); }
/* note: takes ownership of path string */ static void add_non_note(struct notes_tree *t, char *path, unsigned int mode, const unsigned char *sha1) { struct non_note *p = t->prev_non_note, *n; n = (struct non_note *) xmalloc(sizeof(struct non_note)); n->next = NULL; n->path = path; n->mode = mode; hashcpy(n->oid.hash, sha1); t->prev_non_note = n; if (!t->first_non_note) { t->first_non_note = n; return; } if (non_note_cmp(p, n) < 0) ; /* do nothing */ else if (non_note_cmp(t->first_non_note, n) <= 0) p = t->first_non_note; else { /* n sorts before t->first_non_note */ n->next = t->first_non_note; t->first_non_note = n; return; } /* n sorts equal or after p */ while (p->next && non_note_cmp(p->next, n) <= 0) p = p->next; if (non_note_cmp(p, n) == 0) { /* n ~= p; overwrite p with n */ assert(strcmp(p->path, n->path) == 0); p->mode = n->mode; oidcpy(&p->oid, &n->oid); free(n); t->prev_non_note = p; return; } /* n sorts between p and p->next */ n->next = p->next; p->next = n; }
static struct submodule *cache_lookup_name(struct submodule_cache *cache, const struct object_id *gitmodules_oid, const char *name) { struct submodule_entry *entry; unsigned int hash = hash_oid_string(gitmodules_oid, name); struct submodule_entry key; struct submodule key_config; oidcpy(&key_config.gitmodules_oid, gitmodules_oid); key_config.name = name; hashmap_entry_init(&key, hash); key.config = &key_config; entry = hashmap_get(&cache->for_name, &key, NULL); if (entry) return entry->config; return NULL; }
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; }