resolve-undo: record resolved conflicts in a new index extension section
When resolving a conflict using "git add" to create a stage #0 entry, or "git rm" to remove entries at higher stages, remove_index_entry_at() function is eventually called to remove unmerged (i.e. higher stage) entries from the index. Introduce a "resolve_undo_info" structure and keep track of the removed cache entries, and save it in a new index extension section in the index_state. Operations like "read-tree -m", "merge", "checkout [-m] <branch>" and "reset" are signs that recorded information in the index is no longer necessary. The data is removed from the index extension when operations start; they may leave conflicted entries in the index, and later user actions like "git add" will record their conflicted states afresh. Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									be6ff8196d
								
							
						
					
					
						commit
						cfc5789ada
					
				
							
								
								
									
										2
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										2
									
								
								Makefile
								
								
								
								
							|  | @ -483,6 +483,7 @@ LIB_H += reflog-walk.h | ||||||
| LIB_H += refs.h | LIB_H += refs.h | ||||||
| LIB_H += remote.h | LIB_H += remote.h | ||||||
| LIB_H += rerere.h | LIB_H += rerere.h | ||||||
|  | LIB_H += resolve-undo.h | ||||||
| LIB_H += revision.h | LIB_H += revision.h | ||||||
| LIB_H += run-command.h | LIB_H += run-command.h | ||||||
| LIB_H += sha1-lookup.h | LIB_H += sha1-lookup.h | ||||||
|  | @ -578,6 +579,7 @@ LIB_OBJS += refs.o | ||||||
| LIB_OBJS += remote.o | LIB_OBJS += remote.o | ||||||
| LIB_OBJS += replace_object.o | LIB_OBJS += replace_object.o | ||||||
| LIB_OBJS += rerere.o | LIB_OBJS += rerere.o | ||||||
|  | LIB_OBJS += resolve-undo.o | ||||||
| LIB_OBJS += revision.o | LIB_OBJS += revision.o | ||||||
| LIB_OBJS += run-command.o | LIB_OBJS += run-command.o | ||||||
| LIB_OBJS += server-info.o | LIB_OBJS += server-info.o | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| #include "blob.h" | #include "blob.h" | ||||||
| #include "xdiff-interface.h" | #include "xdiff-interface.h" | ||||||
| #include "ll-merge.h" | #include "ll-merge.h" | ||||||
|  | #include "resolve-undo.h" | ||||||
|  |  | ||||||
| static const char * const checkout_usage[] = { | static const char * const checkout_usage[] = { | ||||||
| 	"git checkout [options] <branch>", | 	"git checkout [options] <branch>", | ||||||
|  | @ -370,6 +371,7 @@ static int merge_working_tree(struct checkout_opts *opts, | ||||||
| 	if (read_cache_preload(NULL) < 0) | 	if (read_cache_preload(NULL) < 0) | ||||||
| 		return error("corrupt index file"); | 		return error("corrupt index file"); | ||||||
|  |  | ||||||
|  | 	resolve_undo_clear(); | ||||||
| 	if (opts->force) { | 	if (opts->force) { | ||||||
| 		ret = reset_tree(new->commit->tree, opts, 1); | 		ret = reset_tree(new->commit->tree, opts, 1); | ||||||
| 		if (ret) | 		if (ret) | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ | ||||||
| #include "rerere.h" | #include "rerere.h" | ||||||
| #include "help.h" | #include "help.h" | ||||||
| #include "merge-recursive.h" | #include "merge-recursive.h" | ||||||
|  | #include "resolve-undo.h" | ||||||
|  |  | ||||||
| #define DEFAULT_TWOHEAD (1<<0) | #define DEFAULT_TWOHEAD (1<<0) | ||||||
| #define DEFAULT_OCTOPUS (1<<1) | #define DEFAULT_OCTOPUS (1<<1) | ||||||
|  | @ -604,6 +605,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, | ||||||
| 		discard_cache(); | 		discard_cache(); | ||||||
| 		if (read_cache() < 0) | 		if (read_cache() < 0) | ||||||
| 			die("failed to read the cache"); | 			die("failed to read the cache"); | ||||||
|  | 		resolve_undo_clear(); | ||||||
| 		return ret; | 		return ret; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -851,7 +853,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) | ||||||
| 	if (read_cache_unmerged()) | 	if (read_cache_unmerged()) | ||||||
| 		die("You are in the middle of a conflicted merge." | 		die("You are in the middle of a conflicted merge." | ||||||
| 				" (index unmerged)"); | 				" (index unmerged)"); | ||||||
|  | 	resolve_undo_clear(); | ||||||
| 	/* | 	/* | ||||||
| 	 * Check if we are _not_ on a detached HEAD, i.e. if there is a | 	 * Check if we are _not_ on a detached HEAD, i.e. if there is a | ||||||
| 	 * current branch. | 	 * current branch. | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ | ||||||
| #include "dir.h" | #include "dir.h" | ||||||
| #include "builtin.h" | #include "builtin.h" | ||||||
| #include "parse-options.h" | #include "parse-options.h" | ||||||
|  | #include "resolve-undo.h" | ||||||
|  |  | ||||||
| static int nr_trees; | static int nr_trees; | ||||||
| static struct tree *trees[MAX_UNPACK_TREES]; | static struct tree *trees[MAX_UNPACK_TREES]; | ||||||
|  | @ -122,6 +123,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) | ||||||
| 			die("You need to resolve your current index first"); | 			die("You need to resolve your current index first"); | ||||||
| 		stage = opts.merge = 1; | 		stage = opts.merge = 1; | ||||||
| 	} | 	} | ||||||
|  | 	resolve_undo_clear(); | ||||||
|  |  | ||||||
| 	for (i = 0; i < argc; i++) { | 	for (i = 0; i < argc; i++) { | ||||||
| 		const char *arg = argv[i]; | 		const char *arg = argv[i]; | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								cache.h
								
								
								
								
							
							
						
						
									
										2
									
								
								cache.h
								
								
								
								
							|  | @ -282,6 +282,7 @@ static inline int ce_to_dtype(const struct cache_entry *ce) | ||||||
| struct index_state { | struct index_state { | ||||||
| 	struct cache_entry **cache; | 	struct cache_entry **cache; | ||||||
| 	unsigned int cache_nr, cache_alloc, cache_changed; | 	unsigned int cache_nr, cache_alloc, cache_changed; | ||||||
|  | 	struct string_list *resolve_undo; | ||||||
| 	struct cache_tree *cache_tree; | 	struct cache_tree *cache_tree; | ||||||
| 	struct cache_time timestamp; | 	struct cache_time timestamp; | ||||||
| 	void *alloc; | 	void *alloc; | ||||||
|  | @ -336,6 +337,7 @@ static inline void remove_name_hash(struct cache_entry *ce) | ||||||
| #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) | #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) | ||||||
| #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) | #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) | ||||||
| #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen)) | #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen)) | ||||||
|  | #define resolve_undo_clear() resolve_undo_clear_index(&the_index) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| enum object_type { | enum object_type { | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								read-cache.c
								
								
								
								
							
							
						
						
									
										18
									
								
								read-cache.c
								
								
								
								
							|  | @ -14,6 +14,7 @@ | ||||||
| #include "diffcore.h" | #include "diffcore.h" | ||||||
| #include "revision.h" | #include "revision.h" | ||||||
| #include "blob.h" | #include "blob.h" | ||||||
|  | #include "resolve-undo.h" | ||||||
|  |  | ||||||
| /* Index extensions. | /* Index extensions. | ||||||
|  * |  * | ||||||
|  | @ -26,6 +27,7 @@ | ||||||
|  |  | ||||||
| #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) ) | #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) ) | ||||||
| #define CACHE_EXT_TREE 0x54524545	/* "TREE" */ | #define CACHE_EXT_TREE 0x54524545	/* "TREE" */ | ||||||
|  | #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUN" */ | ||||||
|  |  | ||||||
| struct index_state the_index; | struct index_state the_index; | ||||||
|  |  | ||||||
|  | @ -449,6 +451,7 @@ int remove_index_entry_at(struct index_state *istate, int pos) | ||||||
| { | { | ||||||
| 	struct cache_entry *ce = istate->cache[pos]; | 	struct cache_entry *ce = istate->cache[pos]; | ||||||
|  |  | ||||||
|  | 	record_resolve_undo(istate, ce); | ||||||
| 	remove_name_hash(ce); | 	remove_name_hash(ce); | ||||||
| 	istate->cache_changed = 1; | 	istate->cache_changed = 1; | ||||||
| 	istate->cache_nr--; | 	istate->cache_nr--; | ||||||
|  | @ -1170,6 +1173,9 @@ static int read_index_extension(struct index_state *istate, | ||||||
| 	case CACHE_EXT_TREE: | 	case CACHE_EXT_TREE: | ||||||
| 		istate->cache_tree = cache_tree_read(data, sz); | 		istate->cache_tree = cache_tree_read(data, sz); | ||||||
| 		break; | 		break; | ||||||
|  | 	case CACHE_EXT_RESOLVE_UNDO: | ||||||
|  | 		istate->resolve_undo = resolve_undo_read(data, sz); | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		if (*ext < 'A' || 'Z' < *ext) | 		if (*ext < 'A' || 'Z' < *ext) | ||||||
| 			return error("index uses %.4s extension, which we do not understand", | 			return error("index uses %.4s extension, which we do not understand", | ||||||
|  | @ -1349,6 +1355,7 @@ int is_index_unborn(struct index_state *istate) | ||||||
|  |  | ||||||
| int discard_index(struct index_state *istate) | int discard_index(struct index_state *istate) | ||||||
| { | { | ||||||
|  | 	resolve_undo_clear_index(istate); | ||||||
| 	istate->cache_nr = 0; | 	istate->cache_nr = 0; | ||||||
| 	istate->cache_changed = 0; | 	istate->cache_changed = 0; | ||||||
| 	istate->timestamp.sec = 0; | 	istate->timestamp.sec = 0; | ||||||
|  | @ -1574,6 +1581,17 @@ int write_index(struct index_state *istate, int newfd) | ||||||
| 		if (err) | 		if (err) | ||||||
| 			return -1; | 			return -1; | ||||||
| 	} | 	} | ||||||
|  | 	if (istate->resolve_undo) { | ||||||
|  | 		struct strbuf sb = STRBUF_INIT; | ||||||
|  |  | ||||||
|  | 		resolve_undo_write(&sb, istate->resolve_undo); | ||||||
|  | 		err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO, | ||||||
|  | 					     sb.len) < 0 | ||||||
|  | 			|| ce_write(&c, newfd, sb.buf, sb.len) < 0; | ||||||
|  | 		strbuf_release(&sb); | ||||||
|  | 		if (err) | ||||||
|  | 			return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (ce_flush(&c, newfd) || fstat(newfd, &st)) | 	if (ce_flush(&c, newfd) || fstat(newfd, &st)) | ||||||
| 		return -1; | 		return -1; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,117 @@ | ||||||
|  | #include "cache.h" | ||||||
|  | #include "resolve-undo.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) { | ||||||
|  | 		resolve_undo = xcalloc(1, sizeof(*resolve_undo)); | ||||||
|  | 		resolve_undo->strdup_strings = 1; | ||||||
|  | 		istate->resolve_undo = resolve_undo; | ||||||
|  | 	} | ||||||
|  | 	resolve_undo = istate->resolve_undo; | ||||||
|  | 	lost = string_list_insert(ce->name, resolve_undo); | ||||||
|  | 	if (!lost->util) | ||||||
|  | 		lost->util = xcalloc(1, sizeof(*ui)); | ||||||
|  | 	ui = lost->util; | ||||||
|  | 	hashcpy(ui->sha1[stage - 1], ce->sha1); | ||||||
|  | 	ui->mode[stage - 1] = ce->ce_mode; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int write_one(struct string_list_item *item, void *cbdata) | ||||||
|  | { | ||||||
|  | 	struct strbuf *sb = cbdata; | ||||||
|  | 	struct resolve_undo_info *ui = item->util; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	if (!ui) | ||||||
|  | 		return 0; | ||||||
|  | 	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->sha1[i], 20); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo) | ||||||
|  | { | ||||||
|  | 	for_each_string_list(write_one, resolve_undo, sb); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct string_list *resolve_undo_read(void *data, unsigned long size) | ||||||
|  | { | ||||||
|  | 	struct string_list *resolve_undo; | ||||||
|  | 	size_t len; | ||||||
|  | 	char *endptr; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	resolve_undo = xcalloc(1, sizeof(*resolve_undo)); | ||||||
|  | 	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(data, resolve_undo); | ||||||
|  | 		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 < 20) | ||||||
|  | 				goto error; | ||||||
|  | 			hashcpy(ui->sha1[i], data); | ||||||
|  | 			size -= 20; | ||||||
|  | 			data += 20; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	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 = 1; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | #ifndef RESOLVE_UNDO_H | ||||||
|  | #define RESOLVE_UNDO_H | ||||||
|  |  | ||||||
|  | struct resolve_undo_info { | ||||||
|  | 	unsigned int mode[3]; | ||||||
|  | 	unsigned char sha1[3][20]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | extern void record_resolve_undo(struct index_state *, struct cache_entry *); | ||||||
|  | extern void resolve_undo_write(struct strbuf *, struct string_list *); | ||||||
|  | extern struct string_list *resolve_undo_read(void *, unsigned long); | ||||||
|  | extern void resolve_undo_clear_index(struct index_state *); | ||||||
|  |  | ||||||
|  | #endif | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano