filter-branch: fix ref rewriting with --subdirectory-filter
The previous ancestor discovery code failed on any refs that are (pre-rewrite) ancestors of commits marked for rewriting. This means that in a situation A -- B(topic) -- C(master) where B is dropped by --subdirectory-filter pruning, the 'topic' was not moved up to A as intended, but left unrewritten because we asked about 'git rev-list ^master topic', which does not return anything. Instead, we use the straightforward git rev-list -1 $ref -- $filter_subdir to find the right ancestor. To justify this, note that the nearest ancestor is unique: We use the output of git rev-list --parents -- $filter_subdir to rewrite commits in the first pass, before any ref rewriting. If B is a non-merge commit, the only candidate is its parent. If it is a merge, there are two cases: - All sides of the merge bring the same subdirectory contents. Then rev-list already pruned away the merge in favour for just one of its parents, so there is only one candidate. - Some merge sides, or the merge outcome, differ. Then the merge is not pruned and can be rewritten directly. So it is always safe to use rev-list -1. Signed-off-by: Thomas Rast <trast@student.ethz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									6e84b71237
								
							
						
					
					
						commit
						a0e46390d3
					
				|  | @ -317,24 +317,19 @@ done <../revs | ||||||
|  |  | ||||||
| # In case of a subdirectory filter, it is possible that a specified head | # In case of a subdirectory filter, it is possible that a specified head | ||||||
| # is not in the set of rewritten commits, because it was pruned by the | # is not in the set of rewritten commits, because it was pruned by the | ||||||
| # revision walker.  Fix it by mapping these heads to the next rewritten | # revision walker.  Fix it by mapping these heads to the unique nearest | ||||||
| # ancestor(s), i.e. the boundaries in the set of rewritten commits. | # ancestor that survived the pruning. | ||||||
|  |  | ||||||
| # NEEDSWORK: we should sort the unmapped refs topologically first | if test "$filter_subdir" | ||||||
| while read ref | then | ||||||
| do | 	while read ref | ||||||
| 	sha1=$(git rev-parse "$ref"^0) |  | ||||||
| 	test -f "$workdir"/../map/$sha1 && continue |  | ||||||
| 	# Assign the boundarie(s) in the set of rewritten commits |  | ||||||
| 	# as the replacement commit(s). |  | ||||||
| 	# (This would look a bit nicer if --not --stdin worked.) |  | ||||||
| 	for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") | |  | ||||||
| 		git rev-list $ref --boundary --stdin | |  | ||||||
| 		sed -n "s/^-//p") |  | ||||||
| 	do | 	do | ||||||
| 		map $p >> "$workdir"/../map/$sha1 | 		sha1=$(git rev-parse "$ref"^0) | ||||||
| 	done | 		test -f "$workdir"/../map/$sha1 && continue | ||||||
| done < "$tempdir"/heads | 		ancestor=$(git rev-list -1 $ref -- "$filter_subdir") | ||||||
|  | 		test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1 | ||||||
|  | 	done < "$tempdir"/heads | ||||||
|  | fi | ||||||
|  |  | ||||||
| # Finally update the refs | # Finally update the refs | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -101,7 +101,7 @@ test_expect_success 'filter subdirectory only' ' | ||||||
| 		refs/heads/sub refs/heads/sub-earlier | 		refs/heads/sub refs/heads/sub-earlier | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_expect_failure 'subdirectory filter result looks okay' ' | test_expect_success 'subdirectory filter result looks okay' ' | ||||||
| 	test 2 = $(git rev-list sub | wc -l) && | 	test 2 = $(git rev-list sub | wc -l) && | ||||||
| 	git show sub:new && | 	git show sub:new && | ||||||
| 	test_must_fail git show sub:subdir && | 	test_must_fail git show sub:subdir && | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Thomas Rast
						Thomas Rast