rerere: explain the primary codepath
Explain the internals of rerere as in-code comments, while sprinkling "NEEDSWORK" comment to highlight iffy bits and questionable assumptions. This one covers the codepath reached from rerere(), the primary interface to the subsystem. Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									4b68c2a087
								
							
						
					
					
						commit
						cc899eca55
					
				
							
								
								
									
										95
									
								
								rerere.c
								
								
								
								
							
							
						
						
									
										95
									
								
								rerere.c
								
								
								
								
							|  | @ -206,6 +206,21 @@ static int is_cmarker(char *buf, int marker_char, int marker_size) | ||||||
| 	return isspace(*buf); | 	return isspace(*buf); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Read contents a file with conflicts, normalize the conflicts | ||||||
|  |  * by (1) discarding the common ancestor version in diff3-style, | ||||||
|  |  * (2) reordering our side and their side so that whichever sorts | ||||||
|  |  * alphabetically earlier comes before the other one, while | ||||||
|  |  * computing the "conflict ID", which is just an SHA-1 hash of | ||||||
|  |  * one side of the conflict, NUL, the other side of the conflict, | ||||||
|  |  * and NUL concatenated together. | ||||||
|  |  * | ||||||
|  |  * Return the number of conflict hunks found. | ||||||
|  |  * | ||||||
|  |  * NEEDSWORK: the logic and theory of operation behind this conflict | ||||||
|  |  * normalization may deserve to be documented somewhere, perhaps in | ||||||
|  |  * Documentation/technical/rerere.txt. | ||||||
|  |  */ | ||||||
| static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size) | static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size) | ||||||
| { | { | ||||||
| 	git_SHA_CTX ctx; | 	git_SHA_CTX ctx; | ||||||
|  | @ -276,6 +291,10 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz | ||||||
| 	return hunk_no; | 	return hunk_no; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Scan the path for conflicts, do the "handle_path()" thing above, and | ||||||
|  |  * return the number of conflict hunks found. | ||||||
|  |  */ | ||||||
| static int handle_file(const char *path, unsigned char *sha1, const char *output) | static int handle_file(const char *path, unsigned char *sha1, const char *output) | ||||||
| { | { | ||||||
| 	int hunk_no = 0; | 	int hunk_no = 0; | ||||||
|  | @ -515,29 +534,54 @@ int rerere_remaining(struct string_list *merge_rr) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Find the conflict identified by "name"; the change between its | ||||||
|  |  * "preimage" (i.e. a previous contents with conflict markers) and its | ||||||
|  |  * "postimage" (i.e. the corresponding contents with conflicts | ||||||
|  |  * resolved) may apply cleanly to the contents stored in "path", i.e. | ||||||
|  |  * the conflict this time around. | ||||||
|  |  * | ||||||
|  |  * Returns 0 for successful replay of recorded resolution, or non-zero | ||||||
|  |  * for failure. | ||||||
|  |  */ | ||||||
| static int merge(const char *name, const char *path) | static int merge(const char *name, const char *path) | ||||||
| { | { | ||||||
| 	int ret; | 	int ret; | ||||||
| 	mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0}; | 	mmfile_t cur = {NULL, 0}, base = {NULL, 0}, other = {NULL, 0}; | ||||||
| 	mmbuffer_t result = {NULL, 0}; | 	mmbuffer_t result = {NULL, 0}; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Normalize the conflicts in path and write it out to | ||||||
|  | 	 * "thisimage" temporary file. | ||||||
|  | 	 */ | ||||||
| 	if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0) | 	if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0) | ||||||
| 		return 1; | 		return 1; | ||||||
|  |  | ||||||
| 	if (read_mmfile(&cur, rerere_path(name, "thisimage")) || | 	if (read_mmfile(&cur, rerere_path(name, "thisimage")) || | ||||||
| 			read_mmfile(&base, rerere_path(name, "preimage")) || | 	    read_mmfile(&base, rerere_path(name, "preimage")) || | ||||||
| 			read_mmfile(&other, rerere_path(name, "postimage"))) { | 	    read_mmfile(&other, rerere_path(name, "postimage"))) { | ||||||
| 		ret = 1; | 		ret = 1; | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * A three-way merge. Note that this honors user-customizable | ||||||
|  | 	 * low-level merge driver settings. | ||||||
|  | 	 */ | ||||||
| 	ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL); | 	ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", NULL); | ||||||
| 	if (!ret) { | 	if (!ret) { | ||||||
| 		FILE *f; | 		FILE *f; | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 		 * A successful replay of recorded resolution. | ||||||
|  | 		 * Mark that "postimage" was used to help gc. | ||||||
|  | 		 */ | ||||||
| 		if (utime(rerere_path(name, "postimage"), NULL) < 0) | 		if (utime(rerere_path(name, "postimage"), NULL) < 0) | ||||||
| 			warning("failed utime() on %s: %s", | 			warning("failed utime() on %s: %s", | ||||||
| 					rerere_path(name, "postimage"), | 					rerere_path(name, "postimage"), | ||||||
| 					strerror(errno)); | 					strerror(errno)); | ||||||
|  |  | ||||||
|  | 		/* Update "path" with the resolution */ | ||||||
| 		f = fopen(path, "w"); | 		f = fopen(path, "w"); | ||||||
| 		if (!f) | 		if (!f) | ||||||
| 			return error("Could not open %s: %s", path, | 			return error("Could not open %s: %s", path, | ||||||
|  | @ -590,41 +634,61 @@ static int do_plain_rerere(struct string_list *rr, int fd) | ||||||
| 	find_conflict(&conflict); | 	find_conflict(&conflict); | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * MERGE_RR records paths with conflicts immediately after merge | 	 * MERGE_RR records paths with conflicts immediately after | ||||||
| 	 * failed.  Some of the conflicted paths might have been hand resolved | 	 * merge failed.  Some of the conflicted paths might have been | ||||||
| 	 * in the working tree since then, but the initial run would catch all | 	 * hand resolved in the working tree since then, but the | ||||||
| 	 * and register their preimages. | 	 * initial run would catch all and register their preimages. | ||||||
| 	 */ | 	 */ | ||||||
|  |  | ||||||
| 	for (i = 0; i < conflict.nr; i++) { | 	for (i = 0; i < conflict.nr; i++) { | ||||||
| 		const char *path = conflict.items[i].string; | 		const char *path = conflict.items[i].string; | ||||||
| 		if (!string_list_has_string(rr, path)) { | 		if (!string_list_has_string(rr, path)) { | ||||||
| 			unsigned char sha1[20]; | 			unsigned char sha1[20]; | ||||||
| 			char *hex; | 			char *hex; | ||||||
| 			int ret; | 			int ret; | ||||||
|  |  | ||||||
|  | 			/* | ||||||
|  | 			 * Ask handle_file() to scan and assign a | ||||||
|  | 			 * conflict ID.  No need to write anything out | ||||||
|  | 			 * yet. | ||||||
|  | 			 */ | ||||||
| 			ret = handle_file(path, sha1, NULL); | 			ret = handle_file(path, sha1, NULL); | ||||||
| 			if (ret < 1) | 			if (ret < 1) | ||||||
| 				continue; | 				continue; | ||||||
| 			hex = xstrdup(sha1_to_hex(sha1)); | 			hex = xstrdup(sha1_to_hex(sha1)); | ||||||
| 			string_list_insert(rr, path)->util = hex; | 			string_list_insert(rr, path)->util = hex; | ||||||
|  |  | ||||||
|  | 			/* | ||||||
|  | 			 * If the directory does not exist, create | ||||||
|  | 			 * it.  mkdir_in_gitdir() will fail with | ||||||
|  | 			 * EEXIST if there already is one. | ||||||
|  | 			 * | ||||||
|  | 			 * NEEDSWORK: make sure "gc" does not remove | ||||||
|  | 			 * preimage without removing the directory. | ||||||
|  | 			 */ | ||||||
| 			if (mkdir_in_gitdir(git_path("rr-cache/%s", hex))) | 			if (mkdir_in_gitdir(git_path("rr-cache/%s", hex))) | ||||||
| 				continue; | 				continue; | ||||||
|  |  | ||||||
|  | 			/* | ||||||
|  | 			 * We are the first to encounter this | ||||||
|  | 			 * conflict.  Ask handle_file() to write the | ||||||
|  | 			 * normalized contents to the "preimage" file. | ||||||
|  | 			 */ | ||||||
| 			handle_file(path, NULL, rerere_path(hex, "preimage")); | 			handle_file(path, NULL, rerere_path(hex, "preimage")); | ||||||
| 			fprintf(stderr, "Recorded preimage for '%s'\n", path); | 			fprintf(stderr, "Recorded preimage for '%s'\n", path); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * Now some of the paths that had conflicts earlier might have been | 	 * Some of the paths that had conflicts earlier might have | ||||||
| 	 * hand resolved.  Others may be similar to a conflict already that | 	 * been resolved by the user.  Others may be similar to a | ||||||
| 	 * was resolved before. | 	 * conflict already that was resolved before. | ||||||
| 	 */ | 	 */ | ||||||
|  |  | ||||||
| 	for (i = 0; i < rr->nr; i++) { | 	for (i = 0; i < rr->nr; i++) { | ||||||
| 		int ret; | 		int ret; | ||||||
| 		const char *path = rr->items[i].string; | 		const char *path = rr->items[i].string; | ||||||
| 		const char *name = (const char *)rr->items[i].util; | 		const char *name = (const char *)rr->items[i].util; | ||||||
|  |  | ||||||
|  | 		/* Is there a recorded resolution we could attempt to apply? */ | ||||||
| 		if (has_rerere_resolution(name)) { | 		if (has_rerere_resolution(name)) { | ||||||
| 			if (merge(name, path)) | 			if (merge(name, path)) | ||||||
| 				continue; | 				continue; | ||||||
|  | @ -638,13 +702,13 @@ static int do_plain_rerere(struct string_list *rr, int fd) | ||||||
| 			goto mark_resolved; | 			goto mark_resolved; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/* Let's see if we have resolved it. */ | 		/* Let's see if the user has resolved it. */ | ||||||
| 		ret = handle_file(path, NULL, NULL); | 		ret = handle_file(path, NULL, NULL); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
| 		fprintf(stderr, "Recorded resolution for '%s'.\n", path); |  | ||||||
| 		copy_file(rerere_path(name, "postimage"), path, 0666); | 		copy_file(rerere_path(name, "postimage"), path, 0666); | ||||||
|  | 		fprintf(stderr, "Recorded resolution for '%s'.\n", path); | ||||||
| 	mark_resolved: | 	mark_resolved: | ||||||
| 		free(rr->items[i].util); | 		free(rr->items[i].util); | ||||||
| 		rr->items[i].util = NULL; | 		rr->items[i].util = NULL; | ||||||
|  | @ -698,6 +762,11 @@ int setup_rerere(struct string_list *merge_rr, int flags) | ||||||
| 	return fd; | 	return fd; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * The main entry point that is called internally from codepaths that | ||||||
|  |  * perform mergy operations, possibly leaving conflicted index entries | ||||||
|  |  * and working tree files. | ||||||
|  |  */ | ||||||
| int rerere(int flags) | int rerere(int flags) | ||||||
| { | { | ||||||
| 	struct string_list merge_rr = STRING_LIST_INIT_DUP; | 	struct string_list merge_rr = STRING_LIST_INIT_DUP; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano