fetch: fetch objects by their exact SHA-1 object names
Teach "git fetch" to accept an exact SHA-1 object name the user may obtain out of band on the LHS of a pathspec, and send it on a "want" message when the server side advertises the allow-tip-sha1-in-want capability. Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									390eb36b0a
								
							
						
					
					
						commit
						6e7b66eebd
					
				
							
								
								
									
										22
									
								
								fetch-pack.c
								
								
								
								
							
							
						
						
									
										22
									
								
								fetch-pack.c
								
								
								
								
							|  | @ -36,7 +36,7 @@ static int marked; | |||
| #define MAX_IN_VAIN 256 | ||||
|  | ||||
| static struct commit_list *rev_list; | ||||
| static int non_common_revs, multi_ack, use_sideband; | ||||
| static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want; | ||||
|  | ||||
| static void rev_list_push(struct commit *commit, int mark) | ||||
| { | ||||
|  | @ -563,6 +563,21 @@ static void filter_refs(struct fetch_pack_args *args, | |||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Append unmatched requests to the list */ | ||||
| 	if (allow_tip_sha1_in_want) { | ||||
| 		for (i = 0; i < nr_sought; i++) { | ||||
| 			ref = sought[i]; | ||||
| 			if (ref->matched) | ||||
| 				continue; | ||||
| 			if (get_sha1_hex(ref->name, ref->old_sha1)) | ||||
| 				continue; | ||||
|  | ||||
| 			ref->matched = 1; | ||||
| 			*newtail = ref; | ||||
| 			ref->next = NULL; | ||||
| 			newtail = &ref->next; | ||||
| 		} | ||||
| 	} | ||||
| 	*refs = newlist; | ||||
| } | ||||
|  | ||||
|  | @ -803,6 +818,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, | |||
| 			fprintf(stderr, "Server supports side-band\n"); | ||||
| 		use_sideband = 1; | ||||
| 	} | ||||
| 	if (server_supports("allow-tip-sha1-in-want")) { | ||||
| 		if (args->verbose) | ||||
| 			fprintf(stderr, "Server supports allow-tip-sha1-in-want\n"); | ||||
| 		allow_tip_sha1_in_want = 1; | ||||
| 	} | ||||
| 	if (!server_supports("thin-pack")) | ||||
| 		args->use_thin_pack = 0; | ||||
| 	if (!server_supports("no-progress")) | ||||
|  |  | |||
							
								
								
									
										12
									
								
								remote.c
								
								
								
								
							
							
						
						
									
										12
									
								
								remote.c
								
								
								
								
							|  | @ -15,6 +15,7 @@ static struct refspec s_tag_refspec = { | |||
| 	0, | ||||
| 	1, | ||||
| 	0, | ||||
| 	0, | ||||
| 	"refs/tags/*", | ||||
| 	"refs/tags/*" | ||||
| }; | ||||
|  | @ -565,9 +566,13 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp | |||
| 		flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); | ||||
|  | ||||
| 		if (fetch) { | ||||
| 			unsigned char unused[40]; | ||||
|  | ||||
| 			/* LHS */ | ||||
| 			if (!*rs[i].src) | ||||
| 				; /* empty is ok; it means "HEAD" */ | ||||
| 			else if (llen == 40 && !get_sha1_hex(rs[i].src, unused)) | ||||
| 				rs[i].exact_sha1 = 1; /* ok */ | ||||
| 			else if (!check_refname_format(rs[i].src, flags)) | ||||
| 				; /* valid looking ref is ok */ | ||||
| 			else | ||||
|  | @ -1495,7 +1500,12 @@ int get_fetch_map(const struct ref *remote_refs, | |||
| 	} else { | ||||
| 		const char *name = refspec->src[0] ? refspec->src : "HEAD"; | ||||
|  | ||||
| 		ref_map = get_remote_ref(remote_refs, name); | ||||
| 		if (refspec->exact_sha1) { | ||||
| 			ref_map = alloc_ref(name); | ||||
| 			get_sha1_hex(name, ref_map->old_sha1); | ||||
| 		} else { | ||||
| 			ref_map = get_remote_ref(remote_refs, name); | ||||
| 		} | ||||
| 		if (!missing_ok && !ref_map) | ||||
| 			die("Couldn't find remote ref %s", name); | ||||
| 		if (ref_map) { | ||||
|  |  | |||
							
								
								
									
										1
									
								
								remote.h
								
								
								
								
							
							
						
						
									
										1
									
								
								remote.h
								
								
								
								
							|  | @ -62,6 +62,7 @@ struct refspec { | |||
| 	unsigned force : 1; | ||||
| 	unsigned pattern : 1; | ||||
| 	unsigned matching : 1; | ||||
| 	unsigned exact_sha1 : 1; | ||||
|  | ||||
| 	char *src; | ||||
| 	char *dst; | ||||
|  |  | |||
|  | @ -1064,4 +1064,38 @@ do | |||
| 	' | ||||
| done | ||||
|  | ||||
| test_expect_success 'fetch exact SHA1' ' | ||||
| 	mk_test heads/master hidden/one && | ||||
| 	git push testrepo master:refs/hidden/one && | ||||
| 	( | ||||
| 		cd testrepo && | ||||
| 		git config transfer.hiderefs refs/hidden | ||||
| 	) && | ||||
| 	check_push_result $the_commit hidden/one && | ||||
|  | ||||
| 	mk_child child && | ||||
| 	( | ||||
| 		cd child && | ||||
|  | ||||
| 		# make sure $the_commit does not exist here | ||||
| 		git repack -a -d && | ||||
| 		git prune && | ||||
| 		test_must_fail git cat-file -t $the_commit && | ||||
|  | ||||
| 		# fetching the hidden object should fail by default | ||||
| 		test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy && | ||||
| 		test_must_fail git rev-parse --verify refs/heads/copy && | ||||
|  | ||||
| 		# the server side can allow it to succeed | ||||
| 		( | ||||
| 			cd ../testrepo && | ||||
| 			git config uploadpack.allowtipsha1inwant true | ||||
| 		) && | ||||
|  | ||||
| 		git fetch -v ../testrepo $the_commit:refs/heads/copy && | ||||
| 		result=$(git rev-parse --verify refs/heads/copy) && | ||||
| 		test "$the_commit" = "$result" | ||||
| 	) | ||||
| ' | ||||
|  | ||||
| test_done | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano