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 | #define MAX_IN_VAIN 256 | ||||||
|  |  | ||||||
| static struct commit_list *rev_list; | 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) | 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; | 	*refs = newlist; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -803,6 +818,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, | ||||||
| 			fprintf(stderr, "Server supports side-band\n"); | 			fprintf(stderr, "Server supports side-band\n"); | ||||||
| 		use_sideband = 1; | 		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")) | 	if (!server_supports("thin-pack")) | ||||||
| 		args->use_thin_pack = 0; | 		args->use_thin_pack = 0; | ||||||
| 	if (!server_supports("no-progress")) | 	if (!server_supports("no-progress")) | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								remote.c
								
								
								
								
							
							
						
						
									
										10
									
								
								remote.c
								
								
								
								
							|  | @ -15,6 +15,7 @@ static struct refspec s_tag_refspec = { | ||||||
| 	0, | 	0, | ||||||
| 	1, | 	1, | ||||||
| 	0, | 	0, | ||||||
|  | 	0, | ||||||
| 	"refs/tags/*", | 	"refs/tags/*", | ||||||
| 	"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); | 		flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); | ||||||
|  |  | ||||||
| 		if (fetch) { | 		if (fetch) { | ||||||
|  | 			unsigned char unused[40]; | ||||||
|  |  | ||||||
| 			/* LHS */ | 			/* LHS */ | ||||||
| 			if (!*rs[i].src) | 			if (!*rs[i].src) | ||||||
| 				; /* empty is ok; it means "HEAD" */ | 				; /* 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)) | 			else if (!check_refname_format(rs[i].src, flags)) | ||||||
| 				; /* valid looking ref is ok */ | 				; /* valid looking ref is ok */ | ||||||
| 			else | 			else | ||||||
|  | @ -1495,7 +1500,12 @@ int get_fetch_map(const struct ref *remote_refs, | ||||||
| 	} else { | 	} else { | ||||||
| 		const char *name = refspec->src[0] ? refspec->src : "HEAD"; | 		const char *name = refspec->src[0] ? refspec->src : "HEAD"; | ||||||
|  |  | ||||||
|  | 		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); | 			ref_map = get_remote_ref(remote_refs, name); | ||||||
|  | 		} | ||||||
| 		if (!missing_ok && !ref_map) | 		if (!missing_ok && !ref_map) | ||||||
| 			die("Couldn't find remote ref %s", name); | 			die("Couldn't find remote ref %s", name); | ||||||
| 		if (ref_map) { | 		if (ref_map) { | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								remote.h
								
								
								
								
							
							
						
						
									
										1
									
								
								remote.h
								
								
								
								
							|  | @ -62,6 +62,7 @@ struct refspec { | ||||||
| 	unsigned force : 1; | 	unsigned force : 1; | ||||||
| 	unsigned pattern : 1; | 	unsigned pattern : 1; | ||||||
| 	unsigned matching : 1; | 	unsigned matching : 1; | ||||||
|  | 	unsigned exact_sha1 : 1; | ||||||
|  |  | ||||||
| 	char *src; | 	char *src; | ||||||
| 	char *dst; | 	char *dst; | ||||||
|  |  | ||||||
|  | @ -1064,4 +1064,38 @@ do | ||||||
| 	' | 	' | ||||||
| done | 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 | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano