/* * The user says the trees will be shifted by this much. * Unfortunately we cannot fundamentally tell which one to * be prefixed, as recursive merge can work in either direction. */ void shift_tree_by(const unsigned char *hash1, const unsigned char *hash2, unsigned char *shifted, const char *shift_prefix) { unsigned char sub1[20], sub2[20]; unsigned mode1, mode2; unsigned candidate = 0; /* Can hash2 be a tree at shift_prefix in tree hash1? */ if (!get_tree_entry(hash1, shift_prefix, sub1, &mode1) && S_ISDIR(mode1)) candidate |= 1; /* Can hash1 be a tree at shift_prefix in tree hash2? */ if (!get_tree_entry(hash2, shift_prefix, sub2, &mode2) && S_ISDIR(mode2)) candidate |= 2; if (candidate == 3) { /* Both are plausible -- we need to evaluate the score */ int best_score = score_trees(hash1, hash2); int score; candidate = 0; score = score_trees(sub1, hash2); if (score > best_score) { candidate = 1; best_score = score; } score = score_trees(sub2, hash1); if (score > best_score) candidate = 2; } if (!candidate) { /* Neither is plausible -- do not shift */ hashcpy(shifted, hash2); return; } if (candidate == 1) /* * shift tree2 down by adding shift_prefix above it * to match tree1. */ splice_tree(hash1, shift_prefix, hash2, shifted); else /* * shift tree2 up by removing shift_prefix from it * to match tree1. */ hashcpy(shifted, sub2); }
static struct cache_entry *read_one_ent(const char *which, unsigned char *ent, const char *path, int namelen, int stage) { unsigned mode; unsigned char sha1[20]; int size; struct cache_entry *ce; if (get_tree_entry(ent, path, sha1, &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; } size = cache_entry_size(namelen); ce = xcalloc(1, size); hashcpy(ce->sha1, sha1); 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 int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode) { int namelen = strlen(name); while (t->size) { const char *entry; const unsigned char *sha1; int entrylen, cmp; sha1 = tree_entry_extract(t, &entry, mode); update_tree_entry(t); entrylen = tree_entry_len(entry, sha1); if (entrylen > namelen) continue; cmp = memcmp(name, entry, entrylen); if (cmp > 0) continue; if (cmp < 0) break; if (entrylen == namelen) { hashcpy(result, sha1); return 0; } if (name[entrylen] != '/') continue; if (!S_ISDIR(*mode)) break; if (++entrylen == namelen) { hashcpy(result, sha1); return 0; } return get_tree_entry(sha1, name + entrylen, result, mode); } return -1; }
/* * Returns an index_entry instance which doesn't have to correspond to * a real cache entry in Git's index. */ static struct stage_data *insert_stage_data(const char *path, struct tree *o, struct tree *a, struct tree *b, struct string_list *entries) { struct string_list_item *item; struct stage_data *e = xcalloc(1, sizeof(struct stage_data)); get_tree_entry(o->object.sha1, path, e->stages[1].sha, &e->stages[1].mode); get_tree_entry(a->object.sha1, path, e->stages[2].sha, &e->stages[2].mode); get_tree_entry(b->object.sha1, path, e->stages[3].sha, &e->stages[3].mode); item = string_list_insert(path, entries); item->util = e; return e; }
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 void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, const char *prefix, int remote) { const char *name = argv[0]; const unsigned char *commit_sha1; time_t archive_time; struct tree *tree; const struct commit *commit; unsigned char sha1[20]; /* Remotes are only allowed to fetch actual refs */ if (remote && !remote_allow_unreachable) { char *ref = NULL; const char *colon = strchr(name, ':'); int refnamelen = colon ? colon - name : strlen(name); if (!dwim_ref(name, refnamelen, sha1, &ref)) die("no such ref: %.*s", refnamelen, name); free(ref); } if (get_sha1(name, sha1)) die("Not a valid object name"); commit = lookup_commit_reference_gently(sha1, 1); if (commit) { commit_sha1 = commit->object.sha1; archive_time = commit->date; } else { commit_sha1 = NULL; archive_time = time(NULL); } tree = parse_tree_indirect(sha1); if (tree == NULL) die("not a tree object"); if (prefix) { unsigned char tree_sha1[20]; unsigned int mode; int err; err = get_tree_entry(tree->object.sha1, prefix, tree_sha1, &mode); if (err || !S_ISDIR(mode)) die("current working directory is untracked"); tree = parse_tree_indirect(tree_sha1); } ar_args->tree = tree; ar_args->commit_sha1 = commit_sha1; ar_args->commit = commit; ar_args->time = archive_time; }
static void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, const char *prefix, int remote) { const char *name = argv[0]; const unsigned char *commit_sha1; time_t archive_time; struct tree *tree; const struct commit *commit; struct object_id oid; /* Remotes are only allowed to fetch actual refs */ if (remote && !remote_allow_unreachable) { char *ref = NULL; const char *colon = strchrnul(name, ':'); int refnamelen = colon - name; if (!dwim_ref(name, refnamelen, &oid, &ref)) die(_("no such ref: %.*s"), refnamelen, name); free(ref); } if (get_oid(name, &oid)) die(_("not a valid object name: %s"), name); commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1); if (commit) { commit_sha1 = commit->object.oid.hash; archive_time = commit->date; } else { commit_sha1 = NULL; archive_time = time(NULL); } tree = parse_tree_indirect(&oid); if (tree == NULL) die(_("not a tree object: %s"), oid_to_hex(&oid)); if (prefix) { struct object_id tree_oid; unsigned int mode; int err; err = get_tree_entry(&tree->object.oid, prefix, &tree_oid, &mode); if (err || !S_ISDIR(mode)) die(_("current working directory is untracked")); tree = parse_tree_indirect(&tree_oid); } ar_args->tree = tree; ar_args->commit_sha1 = commit_sha1; ar_args->commit = commit; ar_args->time = archive_time; }
/* * We are trying to come up with a merge between one and two that * results in a tree shape similar to one. The tree two might * correspond to a subtree of one, in which case it needs to be * shifted down by prefixing otherwise empty directories. On the * other hand, it could cover tree one and we might need to pick a * subtree of it. */ void shift_tree(const unsigned char *hash1, const unsigned char *hash2, unsigned char *shifted, int depth_limit) { char *add_prefix; char *del_prefix; int add_score, del_score; /* * NEEDSWORK: this limits the recursion depth to hardcoded * value '2' to avoid excessive overhead. */ if (!depth_limit) depth_limit = 2; add_score = del_score = score_trees(hash1, hash2); add_prefix = xcalloc(1, 1); del_prefix = xcalloc(1, 1); /* * See if one's subtree resembles two; if so we need to prefix * two with a few fake trees to match the prefix. */ match_trees(hash1, hash2, &add_score, &add_prefix, "", depth_limit); /* * See if two's subtree resembles one; if so we need to * pick only subtree of two. */ match_trees(hash2, hash1, &del_score, &del_prefix, "", depth_limit); /* Assume we do not have to do any shifting */ hashcpy(shifted, hash2); if (add_score < del_score) { /* We need to pick a subtree of two */ unsigned mode; if (!*del_prefix) return; if (get_tree_entry(hash2, del_prefix, shifted, &mode)) die("cannot find path %s in tree %s", del_prefix, sha1_to_hex(hash2)); return; } if (!*add_prefix) return; splice_tree(hash1, add_prefix, hash2, shifted); }
/* * Fill the blob_sha1 field of an origin if it hasn't, so that later * call to fill_origin_blob() can use it to locate the data. blob_sha1 * for an origin is also used to pass the blame for the entire file to * the parent to detect the case where a child's blob is identical to * that of its parent's. * * This also fills origin->mode for corresponding tree path. */ static int fill_blob_sha1_and_mode(struct blame_origin *origin) { if (!is_null_oid(&origin->blob_oid)) return 0; if (get_tree_entry(&origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode)) goto error_out; if (oid_object_info(the_repository, &origin->blob_oid, NULL) != OBJ_BLOB) goto error_out; return 0; error_out: oidclr(&origin->blob_oid); origin->mode = S_IFINVALID; return -1; }
static void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, const char *prefix) { const char *name = argv[0]; const unsigned char *commit_sha1; time_t archive_time; struct tree *tree; const struct commit *commit; unsigned char sha1[20]; if (get_sha1(name, sha1)) die("Not a valid object name"); commit = lookup_commit_reference_gently(sha1, 1); if (commit) { commit_sha1 = commit->object.sha1; archive_time = commit->date; } else { commit_sha1 = NULL; archive_time = time(NULL); } tree = parse_tree_indirect(sha1); if (tree == NULL) die("not a tree object"); if (prefix) { unsigned char tree_sha1[20]; unsigned int mode; int err; err = get_tree_entry(tree->object.sha1, prefix, tree_sha1, &mode); if (err || !S_ISDIR(mode)) die("current working directory is untracked"); tree = parse_tree_indirect(tree_sha1); } ar_args->tree = tree; ar_args->commit_sha1 = commit_sha1; ar_args->commit = commit; ar_args->time = archive_time; }
void init_notes(struct notes_tree *t, const char *notes_ref, combine_notes_fn combine_notes, int flags) { struct object_id oid, object_oid; unsigned mode; struct leaf_node root_tree; if (!t) t = &default_notes_tree; assert(!t->initialized); if (!notes_ref) notes_ref = default_notes_ref(); if (!combine_notes) combine_notes = combine_notes_concatenate; t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node)); t->first_non_note = NULL; t->prev_non_note = NULL; t->ref = xstrdup_or_null(notes_ref); t->update_ref = (flags & NOTES_INIT_WRITABLE) ? t->ref : NULL; t->combine_notes = combine_notes; t->initialized = 1; t->dirty = 0; if (flags & NOTES_INIT_EMPTY || !notes_ref || get_oid_treeish(notes_ref, &object_oid)) return; if (flags & NOTES_INIT_WRITABLE && read_ref(notes_ref, &object_oid)) die("Cannot use notes ref %s", notes_ref); if (get_tree_entry(&object_oid, "", &oid, &mode)) die("Failed to read notes tree referenced by %s (%s)", notes_ref, oid_to_hex(&object_oid)); oidclr(&root_tree.key_oid); oidcpy(&root_tree.val_oid, &oid); load_subtree(t, &root_tree, t->root, 0); }
void init_notes(struct notes_tree *t, const char *notes_ref, combine_notes_fn combine_notes, int flags) { unsigned char sha1[20], object_sha1[20]; unsigned mode; struct leaf_node root_tree; if (!t) t = &default_notes_tree; assert(!t->initialized); if (!notes_ref) notes_ref = getenv(GIT_NOTES_REF_ENVIRONMENT); if (!notes_ref) notes_ref = notes_ref_name; /* value of core.notesRef config */ if (!notes_ref) notes_ref = GIT_NOTES_DEFAULT_REF; if (!combine_notes) combine_notes = combine_notes_concatenate; t->root = (struct int_node *) xcalloc(sizeof(struct int_node), 1); t->first_non_note = NULL; t->prev_non_note = NULL; t->ref = notes_ref ? xstrdup(notes_ref) : NULL; t->combine_notes = combine_notes; t->initialized = 1; if (flags & NOTES_INIT_EMPTY || !notes_ref || read_ref(notes_ref, object_sha1)) return; if (get_tree_entry(object_sha1, "", sha1, &mode)) die("Failed to read notes tree referenced by %s (%s)", notes_ref, object_sha1); hashclr(root_tree.key_sha1); hashcpy(root_tree.val_sha1, sha1); load_subtree(t, &root_tree, t->root, 0); }
static void verify_working_tree_path(struct commit *work_tree, const char *path) { struct commit_list *parents; int pos; for (parents = work_tree->parents; parents; parents = parents->next) { const struct object_id *commit_oid = &parents->item->object.oid; struct object_id blob_oid; unsigned mode; if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) && sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB) return; } pos = cache_name_pos(path, strlen(path)); if (pos >= 0) ; /* path is in the index */ else if (-1 - pos < active_nr && !strcmp(active_cache[-1 - pos]->name, path)) ; /* path is in the index, unmerged */ else die("no such path '%s' in HEAD", path); }
void init_notes(struct notes_tree *t, const char *notes_ref, combine_notes_fn combine_notes, int flags) { unsigned char sha1[20], object_sha1[20]; unsigned mode; struct leaf_node root_tree; if (!t) t = &default_notes_tree; assert(!t->initialized); if (!notes_ref) notes_ref = default_notes_ref(); if (!combine_notes) combine_notes = combine_notes_concatenate; t->root = (struct int_node *) xcalloc(1, sizeof(struct int_node)); t->first_non_note = NULL; t->prev_non_note = NULL; t->ref = xstrdup_or_null(notes_ref); t->combine_notes = combine_notes; t->initialized = 1; t->dirty = 0; if (flags & NOTES_INIT_EMPTY || !notes_ref || read_ref(notes_ref, object_sha1)) return; if (get_tree_entry(object_sha1, "", sha1, &mode)) die("Failed to read notes tree referenced by %s (%s)", notes_ref, sha1_to_hex(object_sha1)); hashclr(root_tree.key_sha1); hashcpy(root_tree.val_sha1, sha1); load_subtree(t, &root_tree, t->root, 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; }
static int check_local_mod(struct object_id *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; struct string_list files_staged = STRING_LIST_INIT_NODUP; struct string_list files_cached = STRING_LIST_INIT_NODUP; struct string_list files_local = STRING_LIST_INIT_NODUP; no_head = is_null_oid(head); for (i = 0; i < list.nr; i++) { struct stat st; int pos; const struct cache_entry *ce; const char *name = list.entry[i].name; struct object_id oid; 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 (!is_missing_file_error(errno)) warning_errno(_("failed to stat '%s'"), ce->name); /* 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) && bad_to_remove_submodule(ce->name, SUBMODULE_REMOVAL_DIE_ON_ERROR | SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))) 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->hash, name, oid.hash, &mode) || ce->ce_mode != create_ce_mode(mode) || oidcmp(&ce->oid, &oid)) 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_intent_to_add(ce)) string_list_append(&files_staged, name); } else if (!index_only) { if (staged_changes) string_list_append(&files_cached, name); if (local_changes) string_list_append(&files_local, name); } } print_error_files(&files_staged, Q_("the following file has staged content different " "from both the\nfile and the HEAD:", "the following files have staged content different" " from both the\nfile and the HEAD:", files_staged.nr), _("\n(use -f to force removal)"), &errs); string_list_clear(&files_staged, 0); print_error_files(&files_cached, Q_("the following file has changes " "staged in the index:", "the following files have changes " "staged in the index:", files_cached.nr), _("\n(use --cached to keep the file," " or -f to force removal)"), &errs); string_list_clear(&files_cached, 0); print_error_files(&files_local, Q_("the following file has local modifications:", "the following files have local modifications:", files_local.nr), _("\n(use --cached to keep the file," " or -f to force removal)"), &errs); string_list_clear(&files_local, 0); return errs; }