int diff_merge (SeafCommit *merge, GList **results) { SeafCommit *parent1, *parent2; struct tree_desc t[3]; struct unpack_trees_options opts; struct index_state istate; g_assert (*results == NULL); g_assert (merge->parent_id != NULL && merge->second_parent_id != NULL); parent1 = seaf_commit_manager_get_commit (seaf->commit_mgr, merge->parent_id); if (!parent1) { seaf_warning ("failed to find commit %s.\n", merge->parent_id); return -1; } parent2 = seaf_commit_manager_get_commit (seaf->commit_mgr, merge->second_parent_id); if (!parent2) { seaf_warning ("failed to find commit %s.\n", merge->second_parent_id); seaf_commit_unref (parent1); return -1; } fill_tree_descriptor(&t[0], merge->root_id); fill_tree_descriptor(&t[1], parent1->root_id); fill_tree_descriptor(&t[2], parent2->root_id); seaf_commit_unref (parent1); seaf_commit_unref (parent2); /* Empty index */ memset(&istate, 0, sizeof(istate)); memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; opts.index_only = 1; opts.merge = 1; opts.fn = threeway_diff; opts.unpack_data = results; opts.src_index = &istate; opts.dst_index = NULL; if (unpack_trees(3, t, &opts) < 0) { seaf_warning ("failed to unpack trees.\n"); return -1; } if (*results != NULL) diff_resolve_renames (results); tree_desc_free (&t[0]); tree_desc_free (&t[1]); tree_desc_free (&t[2]); return 0; }
int diff_commits (SeafCommit *commit1, SeafCommit *commit2, GList **results) { struct tree_desc t[2]; struct unpack_trees_options opts; struct index_state istate; g_assert (*results == NULL); if (strcmp (commit1->commit_id, commit2->commit_id) == 0) return 0; if (strcmp (commit1->root_id, EMPTY_SHA1) != 0) { fill_tree_descriptor(&t[0], commit1->root_id); } else { fill_tree_descriptor(&t[0], NULL); } if (strcmp (commit2->root_id, EMPTY_SHA1) != 0) { fill_tree_descriptor(&t[1], commit2->root_id); } else { fill_tree_descriptor(&t[1], NULL); } /* Empty index */ memset(&istate, 0, sizeof(istate)); memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; opts.index_only = 1; opts.merge = 1; opts.fn = twoway_diff; opts.unpack_data = results; opts.src_index = &istate; opts.dst_index = NULL; if (unpack_trees(2, t, &opts) < 0) { seaf_warning ("failed to unpack trees.\n"); return -1; } if (results != NULL) diff_resolve_empty_dirs (results); if (*results != NULL) diff_resolve_renames (results); tree_desc_free (&t[0]); tree_desc_free (&t[1]); return 0; }
static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet) { int nr = 1; int newfd; struct tree_desc desc[2]; struct unpack_trees_options opts; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.src_index = &the_index; opts.dst_index = &the_index; opts.fn = oneway_merge; opts.merge = 1; if (!quiet) opts.verbose_update = 1; switch (reset_type) { case KEEP: case MERGE: opts.update = 1; break; case HARD: opts.update = 1; /* fallthrough */ default: opts.reset = 1; } newfd = hold_locked_index(lock, 1); read_cache_unmerged(); if (reset_type == KEEP) { unsigned char head_sha1[20]; if (get_sha1("HEAD", head_sha1)) return error(_("You do not have a valid HEAD.")); if (!fill_tree_descriptor(desc, head_sha1)) return error(_("Failed to find tree of HEAD.")); nr++; opts.fn = twoway_merge; } if (!fill_tree_descriptor(desc + nr - 1, sha1)) return error(_("Failed to find tree of %s."), sha1_to_hex(sha1)); if (unpack_trees(nr, desc, &opts)) return -1; if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock)) return error(_("Could not write new index file.")); return 0; }
static int reset_index(const unsigned char *sha1, int reset_type, int quiet) { int nr = 1; struct tree_desc desc[2]; struct tree *tree; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.src_index = &the_index; opts.dst_index = &the_index; opts.fn = oneway_merge; opts.merge = 1; if (!quiet) opts.verbose_update = 1; switch (reset_type) { case KEEP: case MERGE: opts.update = 1; break; case HARD: opts.update = 1; /* fallthrough */ default: opts.reset = 1; } read_cache_unmerged(); if (reset_type == KEEP) { unsigned char head_sha1[20]; if (get_sha1("HEAD", head_sha1)) return error(_("You do not have a valid HEAD.")); if (!fill_tree_descriptor(desc, head_sha1)) return error(_("Failed to find tree of HEAD.")); nr++; opts.fn = twoway_merge; } if (!fill_tree_descriptor(desc + nr - 1, sha1)) return error(_("Failed to find tree of %s."), sha1_to_hex(sha1)); if (unpack_trees(nr, desc, &opts)) return -1; if (reset_type == MIXED || reset_type == HARD) { tree = parse_tree_indirect(sha1); prime_cache_tree(&active_cache_tree, tree); } return 0; }
/* * Get the new blocks that need to be checked out if we ff to @remote. */ static int get_new_blocks_ff (SeafRepo *repo, SeafCommit *head, SeafCommit *remote, BlockList **bl) { SeafRepoManager *mgr = repo->manager; char index_path[SEAF_PATH_MAX]; struct tree_desc trees[2]; struct unpack_trees_options topts; struct index_state istate; int ret = 0; memset (&istate, 0, sizeof(istate)); snprintf (index_path, SEAF_PATH_MAX, "%s/%s", mgr->index_dir, repo->id); if (read_index_from (&istate, index_path) < 0) { g_warning ("Failed to load index.\n"); return -1; } fill_tree_descriptor (&trees[0], head->root_id); fill_tree_descriptor (&trees[1], remote->root_id); memset(&topts, 0, sizeof(topts)); topts.base = repo->worktree; topts.head_idx = -1; topts.src_index = &istate; topts.update = 1; topts.merge = 1; topts.fn = twoway_merge; /* unpack_trees() doesn't update index or worktree. */ if (unpack_trees (2, trees, &topts) < 0) { g_warning ("Failed to ff to commit %s.\n", remote->commit_id); ret = -1; goto out; } *bl = block_list_new (); collect_new_blocks_from_index (&topts.result, *bl); out: tree_desc_free (&trees[0]); tree_desc_free (&trees[1]); discard_index (&istate); discard_index (&topts.result); return ret; }
static int seafile_merge_trees(struct merge_options *o, struct unpack_trees_options *opts, SeafDir *common, SeafDir *head, SeafDir *merge, char **error) { int rc; struct tree_desc t[3]; memset(opts, 0, sizeof(*opts)); if (o->call_depth) opts->index_only = 1; else opts->update = 1; opts->merge = 1; opts->head_idx = 2; opts->base = o->worktree; opts->fn = threeway_merge; opts->src_index = o->index; opts->dst_index = o->index; if (o->crypt) opts->crypt = o->crypt; fill_tree_descriptor(t+0, common->dir_id); fill_tree_descriptor(t+1, head->dir_id); fill_tree_descriptor(t+2, merge->dir_id); rc = unpack_trees(3, t, opts); if (rc == 0) { discard_index(o->index); *(o->index) = opts->result; if (o->collect_blocks_only) collect_new_blocks_from_index (o->index, o->bl); } tree_desc_free (t); tree_desc_free (t+1); tree_desc_free (t+2); return rc; }
int diff_index(struct index_state *istate, SeafDir *root, GList **results) { struct tree_desc t; struct unpack_trees_options opts; memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.index_only = 1; /* Unmerged entries are handled in diff worktree. */ opts.skip_unmerged = 1; opts.merge = 1; opts.fn = oneway_diff; opts.unpack_data = results; opts.src_index = istate; opts.dst_index = NULL; fill_tree_descriptor(&t, root->dir_id); int ret = unpack_trees(1, &t, &opts); tree_desc_free (&t); return ret; }
static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info) { int i, ret, bottom; struct tree_desc t[MAX_UNPACK_TREES]; void *buf[MAX_UNPACK_TREES]; struct traverse_info newinfo; struct name_entry *p; p = names; while (!p->mode) p++; newinfo = *info; newinfo.prev = info; newinfo.pathspec = info->pathspec; newinfo.name = *p; newinfo.pathlen += tree_entry_len(p) + 1; newinfo.df_conflicts |= df_conflicts; for (i = 0; i < n; i++, dirmask >>= 1) { const unsigned char *sha1 = NULL; if (dirmask & 1) sha1 = names[i].sha1; buf[i] = fill_tree_descriptor(t+i, sha1); } bottom = switch_cache_bottom(&newinfo); ret = traverse_trees(n, t, &newinfo); restore_cache_bottom(&newinfo, bottom); for (i = 0; i < n; i++) free(buf[i]); return ret; }
static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, struct int_node *node, unsigned int n) { struct object_id object_oid; size_t prefix_len; void *buf; struct tree_desc desc; struct name_entry entry; buf = fill_tree_descriptor(&desc, &subtree->val_oid); if (!buf) die("Could not read %s for notes-index", oid_to_hex(&subtree->val_oid)); prefix_len = subtree->key_oid.hash[KEY_INDEX]; if (prefix_len >= GIT_SHA1_RAWSZ) BUG("prefix_len (%"PRIuMAX") is out of range", (uintmax_t)prefix_len); if (prefix_len * 2 < n) BUG("prefix_len (%"PRIuMAX") is too small", (uintmax_t)prefix_len); memcpy(object_oid.hash, subtree->key_oid.hash, prefix_len); while (tree_entry(&desc, &entry)) { unsigned char type; struct leaf_node *l; size_t path_len = strlen(entry.path); if (path_len == 2 * (GIT_SHA1_RAWSZ - prefix_len)) { /* This is potentially the remainder of the SHA-1 */ if (!S_ISREG(entry.mode)) /* notes must be blobs */ goto handle_non_note; if (hex_to_bytes(object_oid.hash + prefix_len, entry.path, GIT_SHA1_RAWSZ - prefix_len)) goto handle_non_note; /* entry.path is not a SHA1 */ type = PTR_TYPE_NOTE; } else if (path_len == 2) { /* This is potentially an internal node */ size_t len = prefix_len; if (!S_ISDIR(entry.mode)) /* internal nodes must be trees */ goto handle_non_note; if (hex_to_bytes(object_oid.hash + len++, entry.path, 1)) goto handle_non_note; /* entry.path is not a SHA1 */ /* * Pad the rest of the SHA-1 with zeros, * except for the last byte, where we write * the length: */ memset(object_oid.hash + len, 0, GIT_SHA1_RAWSZ - len - 1); object_oid.hash[KEY_INDEX] = (unsigned char)len; type = PTR_TYPE_SUBTREE; } else { /* This can't be part of a note */ goto handle_non_note; } l = xcalloc(1, sizeof(*l)); oidcpy(&l->key_oid, &object_oid); oidcpy(&l->val_oid, &entry.oid); if (note_tree_insert(t, node, n, l, type, combine_notes_concatenate)) die("Failed to load %s %s into notes tree " "from %s", type == PTR_TYPE_NOTE ? "note" : "subtree", oid_to_hex(&l->key_oid), t->ref); continue; handle_non_note: /* * Determine full path for this non-note entry. The * filename is already found in entry.path, but the * directory part of the path must be deduced from the * subtree containing this entry based on our * knowledge that the overall notes tree follows a * strict byte-based progressive fanout structure * (i.e. using 2/38, 2/2/36, etc. fanouts). */ { struct strbuf non_note_path = STRBUF_INIT; const char *q = oid_to_hex(&subtree->key_oid); size_t i; for (i = 0; i < prefix_len; i++) { strbuf_addch(&non_note_path, *q++); strbuf_addch(&non_note_path, *q++); strbuf_addch(&non_note_path, '/'); } strbuf_addstr(&non_note_path, entry.path); add_non_note(t, strbuf_detach(&non_note_path, NULL), entry.mode, entry.oid.hash); } } free(buf); }
static int fast_forward_checkout (SeafRepo *repo, SeafCommit *head, CloneTask *task) { SeafRepoManager *mgr = repo->manager; char index_path[SEAF_PATH_MAX]; struct tree_desc trees[2]; struct unpack_trees_options topts; struct index_state istate; int ret = 0; if (strcmp (head->root_id, task->root_id) == 0) return 0; memset (&istate, 0, sizeof(istate)); snprintf (index_path, SEAF_PATH_MAX, "%s/%s", mgr->index_dir, repo->id); if (read_index_from (&istate, index_path) < 0) { seaf_warning ("Failed to load index.\n"); return -1; } repo->index_corrupted = FALSE; fill_tree_descriptor (&trees[0], task->root_id); fill_tree_descriptor (&trees[1], head->root_id); memset(&topts, 0, sizeof(topts)); topts.base = task->worktree; topts.head_idx = -1; topts.src_index = &istate; topts.update = 1; topts.merge = 1; topts.fn = twoway_merge; if (repo->encrypted) { topts.crypt = seafile_crypt_new (repo->enc_version, repo->enc_key, repo->enc_iv); } if (unpack_trees (2, trees, &topts) < 0) { seaf_warning ("Failed to merge commit %s with work tree.\n", head->commit_id); ret = -1; goto out; } if (update_worktree (&topts, FALSE, head->commit_id, head->creator_name, NULL) < 0) { seaf_warning ("Failed to update worktree.\n"); ret = -1; goto out; } discard_index (&istate); istate = topts.result; if (update_index (&istate, index_path) < 0) { seaf_warning ("Failed to update index.\n"); } out: tree_desc_free (&trees[0]); tree_desc_free (&trees[1]); g_free (topts.crypt); discard_index (&istate); return ret; }
static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, struct int_node *node, unsigned int n) { unsigned char object_sha1[20]; unsigned int prefix_len; void *buf; struct tree_desc desc; struct name_entry entry; int len, path_len; unsigned char type; struct leaf_node *l; buf = fill_tree_descriptor(&desc, subtree->val_sha1); if (!buf) die("Could not read %s for notes-index", sha1_to_hex(subtree->val_sha1)); prefix_len = subtree->key_sha1[19]; assert(prefix_len * 2 >= n); memcpy(object_sha1, subtree->key_sha1, prefix_len); while (tree_entry(&desc, &entry)) { path_len = strlen(entry.path); len = get_sha1_hex_segment(entry.path, path_len, object_sha1 + prefix_len, 20 - prefix_len); if (len < 0) goto handle_non_note; /* entry.path is not a SHA1 */ len += prefix_len; /* * If object SHA1 is complete (len == 20), assume note object * If object SHA1 is incomplete (len < 20), and current * component consists of 2 hex chars, assume note subtree */ if (len <= 20) { type = PTR_TYPE_NOTE; l = (struct leaf_node *) xcalloc(sizeof(struct leaf_node), 1); hashcpy(l->key_sha1, object_sha1); hashcpy(l->val_sha1, entry.sha1); if (len < 20) { if (!S_ISDIR(entry.mode) || path_len != 2) goto handle_non_note; /* not subtree */ l->key_sha1[19] = (unsigned char) len; type = PTR_TYPE_SUBTREE; } note_tree_insert(t, node, n, l, type, combine_notes_concatenate); } continue; handle_non_note: /* * Determine full path for this non-note entry: * The filename is already found in entry.path, but the * directory part of the path must be deduced from the subtree * containing this entry. We assume here that the overall notes * tree follows a strict byte-based progressive fanout * structure (i.e. using 2/38, 2/2/36, etc. fanouts, and not * e.g. 4/36 fanout). This means that if a non-note is found at * path "dead/beef", the following code will register it as * being found on "de/ad/beef". * On the other hand, if you use such non-obvious non-note * paths in the middle of a notes tree, you deserve what's * coming to you ;). Note that for non-notes that are not * SHA1-like at the top level, there will be no problems. * * To conclude, it is strongly advised to make sure non-notes * have at least one non-hex character in the top-level path * component. */ { char non_note_path[PATH_MAX]; char *p = non_note_path; const char *q = sha1_to_hex(subtree->key_sha1); int i; for (i = 0; i < prefix_len; i++) { *p++ = *q++; *p++ = *q++; *p++ = '/'; } strcpy(p, entry.path); add_non_note(t, non_note_path, entry.mode, entry.sha1); } } free(buf); }
static struct combine_diff_path *ll_diff_tree_paths( struct combine_diff_path *p, const unsigned char *sha1, const unsigned char **parents_sha1, int nparent, struct strbuf *base, struct diff_options *opt) { struct tree_desc t, *tp; void *ttree, **tptree; int i; FAST_ARRAY_ALLOC(tp, nparent); FAST_ARRAY_ALLOC(tptree, nparent); /* * load parents first, as they are probably already cached. * * ( log_tree_diff() parses commit->parent before calling here via * diff_tree_sha1(parent, commit) ) */ for (i = 0; i < nparent; ++i) tptree[i] = fill_tree_descriptor(&tp[i], parents_sha1[i]); ttree = fill_tree_descriptor(&t, sha1); /* Enable recursion indefinitely */ opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE); for (;;) { int imin, cmp; if (diff_can_quit_early(opt)) break; if (opt->pathspec.nr) { skip_uninteresting(&t, base, opt); for (i = 0; i < nparent; i++) skip_uninteresting(&tp[i], base, opt); } /* comparing is finished when all trees are done */ if (!t.size) { int done = 1; for (i = 0; i < nparent; ++i) if (tp[i].size) { done = 0; break; } if (done) break; } /* * lookup imin = argmin(p1...pn), * mark entries whether they =p[imin] along the way */ imin = 0; tp[0].entry.mode &= ~S_IFXMIN_NEQ; for (i = 1; i < nparent; ++i) { cmp = tree_entry_pathcmp(&tp[i], &tp[imin]); if (cmp < 0) { imin = i; tp[i].entry.mode &= ~S_IFXMIN_NEQ; } else if (cmp == 0) { tp[i].entry.mode &= ~S_IFXMIN_NEQ; } else { tp[i].entry.mode |= S_IFXMIN_NEQ; } } /* fixup markings for entries before imin */ for (i = 0; i < imin; ++i) tp[i].entry.mode |= S_IFXMIN_NEQ; /* pi > p[imin] */ /* compare t vs p[imin] */ cmp = tree_entry_pathcmp(&t, &tp[imin]); /* t = p[imin] */ if (cmp == 0) { /* are either pi > p[imin] or diff(t,pi) != ø ? */ if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) { for (i = 0; i < nparent; ++i) { /* p[i] > p[imin] */ if (tp[i].entry.mode & S_IFXMIN_NEQ) continue; /* diff(t,pi) != ø */ if (oidcmp(t.entry.oid, tp[i].entry.oid) || (t.entry.mode != tp[i].entry.mode)) continue; goto skip_emit_t_tp; } } /* D += {δ(t,pi) if pi=p[imin]; "+a" if pi > p[imin]} */ p = emit_path(p, base, opt, nparent, &t, tp, imin); skip_emit_t_tp: /* t↓, ∀ pi=p[imin] pi↓ */ update_tree_entry(&t); update_tp_entries(tp, nparent); } /* t < p[imin] */ else if (cmp < 0) { /* D += "+t" */ p = emit_path(p, base, opt, nparent, &t, /*tp=*/NULL, -1); /* t↓ */ update_tree_entry(&t); } /* t > p[imin] */ else { /* ∀i pi=p[imin] -> D += "-p[imin]" */ if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) { for (i = 0; i < nparent; ++i) if (tp[i].entry.mode & S_IFXMIN_NEQ) goto skip_emit_tp; } p = emit_path(p, base, opt, nparent, /*t=*/NULL, tp, imin); skip_emit_tp: /* ∀ pi=p[imin] pi↓ */ update_tp_entries(tp, nparent); } } free(ttree); for (i = nparent-1; i >= 0; i--) free(tptree[i]); FAST_ARRAY_FREE(tptree, nparent); FAST_ARRAY_FREE(tp, nparent); return p; }
static int reset_index(const struct object_id *oid, int reset_type, int quiet) { int i, nr = 0; struct tree_desc desc[2]; struct tree *tree; struct unpack_trees_options opts; int ret = -1; memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.src_index = &the_index; opts.dst_index = &the_index; opts.fn = oneway_merge; opts.merge = 1; if (!quiet) opts.verbose_update = 1; switch (reset_type) { case KEEP: case MERGE: opts.update = 1; break; case HARD: opts.update = 1; /* fallthrough */ default: opts.reset = 1; } read_cache_unmerged(); if (reset_type == KEEP) { struct object_id head_oid; if (get_oid("HEAD", &head_oid)) return error(_("You do not have a valid HEAD.")); if (!fill_tree_descriptor(desc + nr, &head_oid)) return error(_("Failed to find tree of HEAD.")); nr++; opts.fn = twoway_merge; } if (!fill_tree_descriptor(desc + nr, oid)) { error(_("Failed to find tree of %s."), oid_to_hex(oid)); goto out; } nr++; if (unpack_trees(nr, desc, &opts)) goto out; if (reset_type == MIXED || reset_type == HARD) { tree = parse_tree_indirect(oid); prime_cache_tree(&the_index, tree); } ret = 0; out: for (i = 0; i < nr; i++) free((void *)desc[i].buffer); return ret; }