git/resolve-undo.c

176 lines
4.1 KiB
C
Raw Normal View History

#include "git-compat-util.h"
#include "dir.h"
#include "hash.h"
#include "read-cache.h"
#include "resolve-undo.h"
#include "sparse-index.h"
#include "string-list.h"
/* The only error case is to run out of memory in string-list */
void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)
{
struct string_list_item *lost;
struct resolve_undo_info *ui;
struct string_list *resolve_undo;
int stage = ce_stage(ce);
if (!stage)
return;
if (!istate->resolve_undo) {
CALLOC_ARRAY(resolve_undo, 1);
resolve_undo->strdup_strings = 1;
istate->resolve_undo = resolve_undo;
}
resolve_undo = istate->resolve_undo;
lost = string_list_insert(resolve_undo, ce->name);
if (!lost->util)
lost->util = xcalloc(1, sizeof(*ui));
ui = lost->util;
oidcpy(&ui->oid[stage - 1], &ce->oid);
ui->mode[stage - 1] = ce->ce_mode;
}
void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
{
struct string_list_item *item;
for_each_string_list_item(item, resolve_undo) {
struct resolve_undo_info *ui = item->util;
int i;
if (!ui)
continue;
strbuf_addstr(sb, item->string);
strbuf_addch(sb, 0);
for (i = 0; i < 3; i++)
strbuf_addf(sb, "%o%c", ui->mode[i], 0);
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
continue;
strbuf_add(sb, ui->oid[i].hash, the_hash_algo->rawsz);
}
}
}
struct string_list *resolve_undo_read(const char *data, unsigned long size)
{
struct string_list *resolve_undo;
size_t len;
char *endptr;
int i;
const unsigned rawsz = the_hash_algo->rawsz;
CALLOC_ARRAY(resolve_undo, 1);
resolve_undo->strdup_strings = 1;
while (size) {
struct string_list_item *lost;
struct resolve_undo_info *ui;
len = strlen(data) + 1;
if (size <= len)
goto error;
lost = string_list_insert(resolve_undo, data);
if (!lost->util)
lost->util = xcalloc(1, sizeof(*ui));
ui = lost->util;
size -= len;
data += len;
for (i = 0; i < 3; i++) {
ui->mode[i] = strtoul(data, &endptr, 8);
if (!endptr || endptr == data || *endptr)
goto error;
len = (endptr + 1) - (char*)data;
if (size <= len)
goto error;
size -= len;
data += len;
}
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
continue;
if (size < rawsz)
goto error;
oidread(&ui->oid[i], (const unsigned char *)data);
size -= rawsz;
data += rawsz;
}
}
return resolve_undo;
error:
string_list_clear(resolve_undo, 1);
error("Index records invalid resolve-undo information");
return NULL;
}
void resolve_undo_clear_index(struct index_state *istate)
{
struct string_list *resolve_undo = istate->resolve_undo;
if (!resolve_undo)
return;
string_list_clear(resolve_undo, 1);
free(resolve_undo);
istate->resolve_undo = NULL;
istate->cache_changed |= RESOLVE_UNDO_CHANGED;
}
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
int unmerge_index_entry(struct index_state *istate, const char *path,
struct resolve_undo_info *ru, unsigned ce_flags)
{
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
int i = index_name_pos(istate, path, strlen(path));
if (i < 0) {
/* unmerged? */
i = -i - 1;
if (i < istate->cache_nr &&
!strcmp(istate->cache[i]->name, path))
/* yes, it is already unmerged */
return 0;
/* fallthru: resolved to removal */
} else {
/* merged - remove it to replace it with unmerged entries */
remove_index_entry_at(istate, i);
}
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
for (i = 0; i < 3; i++) {
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
struct cache_entry *ce;
if (!ru->mode[i])
continue;
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
ce = make_cache_entry(istate, ru->mode[i], &ru->oid[i],
path, i + 1, 0);
ce->ce_flags |= ce_flags;
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD))
return error("cannot unmerge '%s'", path);
}
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
return 0;
}
void unmerge_index(struct index_state *istate, const struct pathspec *pathspec,
unsigned ce_flags)
checkout: avoid unnecessary match_pathspec calls In checkout_paths() we do this - for all updated items, call match_pathspec - for all items, call match_pathspec (inside unmerge_cache) - for all items, call match_pathspec (for showing "path .. is unmerged) - for updated items, call match_pathspec and update paths That's a lot of duplicate match_pathspec(s) and the function is not exactly cheap to be called so many times, especially on large indexes. This patch makes it call match_pathspec once per updated index entry, save the result in ce_flags and reuse the results in the following loops. The changes in 0a1283b (checkout $tree $path: do not clobber local changes in $path not in $tree - 2011-09-30) limit the affected paths to ones we read from $tree. We do not do anything to other modified entries in this case, so the "for all items" above could be modified to "for all updated items". But.. The command's behavior now is modified slightly: unmerged entries that match $path, but not updated by $tree, are now NOT touched. Although this should be considered a bug fix, not a regression. A new test is added for this change. And while at there, free ps_matched after use. The following command is tested on webkit, 215k entries. The pattern is chosen mainly to make match_pathspec sweat: git checkout -- "*[a-zA-Z]*[a-zA-Z]*[a-zA-Z]*" before after real 0m3.493s 0m2.737s user 0m2.239s 0m1.586s sys 0m1.252s 0m1.151s Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-03-27 13:58:21 +08:00
{
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
struct string_list_item *item;
checkout: avoid unnecessary match_pathspec calls In checkout_paths() we do this - for all updated items, call match_pathspec - for all items, call match_pathspec (inside unmerge_cache) - for all items, call match_pathspec (for showing "path .. is unmerged) - for updated items, call match_pathspec and update paths That's a lot of duplicate match_pathspec(s) and the function is not exactly cheap to be called so many times, especially on large indexes. This patch makes it call match_pathspec once per updated index entry, save the result in ce_flags and reuse the results in the following loops. The changes in 0a1283b (checkout $tree $path: do not clobber local changes in $path not in $tree - 2011-09-30) limit the affected paths to ones we read from $tree. We do not do anything to other modified entries in this case, so the "for all items" above could be modified to "for all updated items". But.. The command's behavior now is modified slightly: unmerged entries that match $path, but not updated by $tree, are now NOT touched. Although this should be considered a bug fix, not a regression. A new test is added for this change. And while at there, free ps_matched after use. The following command is tested on webkit, 215k entries. The pattern is chosen mainly to make match_pathspec sweat: git checkout -- "*[a-zA-Z]*[a-zA-Z]*[a-zA-Z]*" before after real 0m3.493s 0m2.737s user 0m2.239s 0m1.586s sys 0m1.252s 0m1.151s Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-03-27 13:58:21 +08:00
if (!istate->resolve_undo)
return;
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(istate);
checkout: avoid unnecessary match_pathspec calls In checkout_paths() we do this - for all updated items, call match_pathspec - for all items, call match_pathspec (inside unmerge_cache) - for all items, call match_pathspec (for showing "path .. is unmerged) - for updated items, call match_pathspec and update paths That's a lot of duplicate match_pathspec(s) and the function is not exactly cheap to be called so many times, especially on large indexes. This patch makes it call match_pathspec once per updated index entry, save the result in ce_flags and reuse the results in the following loops. The changes in 0a1283b (checkout $tree $path: do not clobber local changes in $path not in $tree - 2011-09-30) limit the affected paths to ones we read from $tree. We do not do anything to other modified entries in this case, so the "for all items" above could be modified to "for all updated items". But.. The command's behavior now is modified slightly: unmerged entries that match $path, but not updated by $tree, are now NOT touched. Although this should be considered a bug fix, not a regression. A new test is added for this change. And while at there, free ps_matched after use. The following command is tested on webkit, 215k entries. The pattern is chosen mainly to make match_pathspec sweat: git checkout -- "*[a-zA-Z]*[a-zA-Z]*[a-zA-Z]*" before after real 0m3.493s 0m2.737s user 0m2.239s 0m1.586s sys 0m1.252s 0m1.151s Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-03-27 13:58:21 +08:00
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
for_each_string_list_item(item, istate->resolve_undo) {
const char *path = item->string;
struct resolve_undo_info *ru = item->util;
if (!item->util)
continue;
if (!match_pathspec(istate, pathspec,
item->string, strlen(item->string),
0, NULL, 0))
continue;
unmerge_index_entry(istate, path, ru, ce_flags);
resolve-undo: allow resurrecting conflicted state that resolved to deletion The resolve-undo index extension records up to three (mode, object name) tuples for non-zero stages for each path that was resolved, to be used to recreate the original conflicted state later when the user requests. The unmerge_index_entry_at() function uses the resolve-undo data to do so, but it assumes that the path for which the conflicted state needs to be recreated can be specified by the position in the active_cache[] array. This obviously cannot salvage the state of conflicted paths that were resolved by removing them. For example, a delete-modify conflict, in which the change whose "modify" side made is a trivial typofix, may legitimately be resolved to remove the path, and resolve-undo extension does record the two (mode, object name) tuples for the common ancestor version and their version, lacking our version. But after recording such a removal of the path, you should be able to use resolve-undo data to recreate the conflicted state. Introduce a new unmerge_index_entry() helper function that takes the path (which does not necessarily have to exist in the active_cache[] array) and resolve-undo data, and use it to reimplement unmerge_index() public function that is used by "git rerere". The limited interface is still kept for now, as it is used by "git checkout -m" and "git update-index --unmerge", but these two codepaths will be updated to lift the assumption to allow conflicts that resolved to deletion can be recreated. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-08-01 06:44:04 +08:00
free(ru);
item->util = NULL;
}
}