get_sha1: support $commit^{/regex} syntax
This works like ":/regex" syntax that finds a recently created commit starting from all refs, but limits the discovery to those reachable from the named commit. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									84baa31bcb
								
							
						
					
					
						commit
						32574b68c5
					
				| 
						 | 
				
			
			@ -106,6 +106,12 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
 | 
			
		|||
  and dereference the tag recursively until a non-tag object is
 | 
			
		||||
  found.
 | 
			
		||||
 | 
			
		||||
* A suffix '{caret}' to a revision parameter followed by a brace
 | 
			
		||||
  pair that contains a text led by a slash (e.g. `HEAD^{/fix nasty bug}`):
 | 
			
		||||
  this is the same as `:/fix nasty bug` syntax below except that
 | 
			
		||||
  it returns the youngest matching commit which is reachable from
 | 
			
		||||
  the ref before '{caret}'.
 | 
			
		||||
 | 
			
		||||
* A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names
 | 
			
		||||
  a commit whose commit message matches the specified regular expression.
 | 
			
		||||
  This name returns the youngest matching commit which is
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										37
									
								
								sha1_name.c
								
								
								
								
							
							
						
						
									
										37
									
								
								sha1_name.c
								
								
								
								
							| 
						 | 
				
			
			@ -7,6 +7,8 @@
 | 
			
		|||
#include "refs.h"
 | 
			
		||||
#include "remote.h"
 | 
			
		||||
 | 
			
		||||
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 | 
			
		||||
 | 
			
		||||
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
 | 
			
		||||
{
 | 
			
		||||
	struct alternate_object_database *alt;
 | 
			
		||||
| 
						 | 
				
			
			@ -562,6 +564,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
 | 
			
		|||
		expected_type = OBJ_BLOB;
 | 
			
		||||
	else if (sp[0] == '}')
 | 
			
		||||
		expected_type = OBJ_NONE;
 | 
			
		||||
	else if (sp[0] == '/')
 | 
			
		||||
		expected_type = OBJ_COMMIT;
 | 
			
		||||
	else
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -576,19 +580,30 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
 | 
			
		|||
		if (!o || (!o->parsed && !parse_object(o->sha1)))
 | 
			
		||||
			return -1;
 | 
			
		||||
		hashcpy(sha1, o->sha1);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		/*
 | 
			
		||||
		 * At this point, the syntax look correct, so
 | 
			
		||||
		 * if we do not get the needed object, we should
 | 
			
		||||
		 * barf.
 | 
			
		||||
		 */
 | 
			
		||||
		o = peel_to_type(name, len, o, expected_type);
 | 
			
		||||
		if (o) {
 | 
			
		||||
			hashcpy(sha1, o->sha1);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * At this point, the syntax look correct, so
 | 
			
		||||
	 * if we do not get the needed object, we should
 | 
			
		||||
	 * barf.
 | 
			
		||||
	 */
 | 
			
		||||
	o = peel_to_type(name, len, o, expected_type);
 | 
			
		||||
	if (!o)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	hashcpy(sha1, o->sha1);
 | 
			
		||||
	if (sp[0] == '/') {
 | 
			
		||||
		/* "$commit^{/foo}" */
 | 
			
		||||
		char *prefix;
 | 
			
		||||
		int ret;
 | 
			
		||||
		struct commit_list *list = NULL;
 | 
			
		||||
 | 
			
		||||
		prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
 | 
			
		||||
		commit_list_insert((struct commit *)o, &list);
 | 
			
		||||
		ret = get_sha1_oneline(prefix, sha1, list);
 | 
			
		||||
		free(prefix);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
test_description='tests for ref^{stuff}'
 | 
			
		||||
 | 
			
		||||
. ./test-lib.sh
 | 
			
		||||
 | 
			
		||||
test_expect_success 'setup' '
 | 
			
		||||
	echo blob >a-blob &&
 | 
			
		||||
	git tag -a -m blob blob-tag `git hash-object -w a-blob`
 | 
			
		||||
	mkdir a-tree &&
 | 
			
		||||
	echo moreblobs >a-tree/another-blob &&
 | 
			
		||||
	git add . &&
 | 
			
		||||
	TREE_SHA1=`git write-tree` &&
 | 
			
		||||
	git tag -a -m tree tree-tag "$TREE_SHA1" &&
 | 
			
		||||
	git commit -m Initial &&
 | 
			
		||||
	git tag -a -m commit commit-tag &&
 | 
			
		||||
	git branch ref &&
 | 
			
		||||
	git checkout master &&
 | 
			
		||||
	echo modified >>a-blob &&
 | 
			
		||||
	git add -u &&
 | 
			
		||||
	git commit -m Modified
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_expect_success 'ref^{non-existent}' '
 | 
			
		||||
	test_must_fail git rev-parse ref^{non-existent}
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_expect_success 'ref^{}' '
 | 
			
		||||
	git rev-parse ref >expected &&
 | 
			
		||||
	git rev-parse ref^{} >actual &&
 | 
			
		||||
	test_cmp expected actual &&
 | 
			
		||||
	git rev-parse commit-tag^{} >actual &&
 | 
			
		||||
	test_cmp expected actual
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_expect_success 'ref^{commit}' '
 | 
			
		||||
	git rev-parse ref >expected &&
 | 
			
		||||
	git rev-parse ref^{commit} >actual &&
 | 
			
		||||
	test_cmp expected actual &&
 | 
			
		||||
	git rev-parse commit-tag^{commit} >actual &&
 | 
			
		||||
	test_cmp expected actual &&
 | 
			
		||||
	test_must_fail git rev-parse tree-tag^{commit} &&
 | 
			
		||||
	test_must_fail git rev-parse blob-tag^{commit}
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_expect_success 'ref^{tree}' '
 | 
			
		||||
	echo $TREE_SHA1 >expected &&
 | 
			
		||||
	git rev-parse ref^{tree} >actual &&
 | 
			
		||||
	test_cmp expected actual &&
 | 
			
		||||
	git rev-parse commit-tag^{tree} >actual &&
 | 
			
		||||
	test_cmp expected actual &&
 | 
			
		||||
	git rev-parse tree-tag^{tree} >actual &&
 | 
			
		||||
	test_cmp expected actual &&
 | 
			
		||||
	test_must_fail git rev-parse blob-tag^{tree}
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_expect_success 'ref^{/.}' '
 | 
			
		||||
	git rev-parse master >expected &&
 | 
			
		||||
	git rev-parse master^{/.} >actual &&
 | 
			
		||||
	test_cmp expected actual
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_expect_success 'ref^{/non-existent}' '
 | 
			
		||||
	test_must_fail git rev-parse master^{/non-existent}
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_expect_success 'ref^{/Initial}' '
 | 
			
		||||
	git rev-parse ref >expected &&
 | 
			
		||||
	git rev-parse master^{/Initial} >actual &&
 | 
			
		||||
	test_cmp expected actual
 | 
			
		||||
'
 | 
			
		||||
 | 
			
		||||
test_done
 | 
			
		||||
		Loading…
	
		Reference in New Issue