Merge branch 'nd/oneline-sha1-name-from-specific-ref'
* nd/oneline-sha1-name-from-specific-ref: get_sha1: handle special case $commit^{/} get_sha1: support $commit^{/regex} syntax get_sha1_oneline: make callers prepare the commit list to traverse get_sha1_oneline: fix lifespan rule of temp_commit_buffer variable
This commit is contained in:
commit
41cd7974b0
@ -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
|
and dereference the tag recursively until a non-tag object is
|
||||||
found.
|
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 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.
|
a commit whose commit message matches the specified regular expression.
|
||||||
This name returns the youngest matching commit which is
|
This name returns the youngest matching commit which is
|
||||||
|
87
sha1_name.c
87
sha1_name.c
@ -7,6 +7,8 @@
|
|||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "remote.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)
|
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
|
||||||
{
|
{
|
||||||
struct alternate_object_database *alt;
|
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;
|
expected_type = OBJ_BLOB;
|
||||||
else if (sp[0] == '}')
|
else if (sp[0] == '}')
|
||||||
expected_type = OBJ_NONE;
|
expected_type = OBJ_NONE;
|
||||||
|
else if (sp[0] == '/')
|
||||||
|
expected_type = OBJ_COMMIT;
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -576,19 +580,37 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
|
|||||||
if (!o || (!o->parsed && !parse_object(o->sha1)))
|
if (!o || (!o->parsed && !parse_object(o->sha1)))
|
||||||
return -1;
|
return -1;
|
||||||
hashcpy(sha1, o->sha1);
|
hashcpy(sha1, o->sha1);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
/*
|
/*
|
||||||
* At this point, the syntax look correct, so
|
* At this point, the syntax look correct, so
|
||||||
* if we do not get the needed object, we should
|
* if we do not get the needed object, we should
|
||||||
* barf.
|
* barf.
|
||||||
*/
|
*/
|
||||||
o = peel_to_type(name, len, o, expected_type);
|
o = peel_to_type(name, len, o, expected_type);
|
||||||
if (o) {
|
if (!o)
|
||||||
hashcpy(sha1, o->sha1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
hashcpy(sha1, o->sha1);
|
||||||
|
if (sp[0] == '/') {
|
||||||
|
/* "$commit^{/foo}" */
|
||||||
|
char *prefix;
|
||||||
|
int ret;
|
||||||
|
struct commit_list *list = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $commit^{/}. Some regex implementation may reject.
|
||||||
|
* We don't need regex anyway. '' pattern always matches.
|
||||||
|
*/
|
||||||
|
if (sp[1] == '}')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -686,15 +708,14 @@ static int handle_one_ref(const char *path,
|
|||||||
if (object->type != OBJ_COMMIT)
|
if (object->type != OBJ_COMMIT)
|
||||||
return 0;
|
return 0;
|
||||||
insert_by_date((struct commit *)object, list);
|
insert_by_date((struct commit *)object, list);
|
||||||
object->flags |= ONELINE_SEEN;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
|
static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
|
||||||
|
struct commit_list *list)
|
||||||
{
|
{
|
||||||
struct commit_list *list = NULL, *backup = NULL, *l;
|
struct commit_list *backup = NULL, *l;
|
||||||
int retval = -1;
|
int found = 0;
|
||||||
char *temp_commit_buffer = NULL;
|
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
|
|
||||||
if (prefix[0] == '!') {
|
if (prefix[0] == '!') {
|
||||||
@ -706,41 +727,45 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
|
|||||||
if (regcomp(®ex, prefix, REG_EXTENDED))
|
if (regcomp(®ex, prefix, REG_EXTENDED))
|
||||||
die("Invalid search pattern: %s", prefix);
|
die("Invalid search pattern: %s", prefix);
|
||||||
|
|
||||||
for_each_ref(handle_one_ref, &list);
|
for (l = list; l; l = l->next) {
|
||||||
for (l = list; l; l = l->next)
|
l->item->object.flags |= ONELINE_SEEN;
|
||||||
commit_list_insert(l->item, &backup);
|
commit_list_insert(l->item, &backup);
|
||||||
|
}
|
||||||
while (list) {
|
while (list) {
|
||||||
char *p;
|
char *p, *to_free = NULL;
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
|
int matches;
|
||||||
|
|
||||||
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
|
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
|
||||||
if (!parse_object(commit->object.sha1))
|
if (!parse_object(commit->object.sha1))
|
||||||
continue;
|
continue;
|
||||||
free(temp_commit_buffer);
|
|
||||||
if (commit->buffer)
|
if (commit->buffer)
|
||||||
p = commit->buffer;
|
p = commit->buffer;
|
||||||
else {
|
else {
|
||||||
p = read_sha1_file(commit->object.sha1, &type, &size);
|
p = read_sha1_file(commit->object.sha1, &type, &size);
|
||||||
if (!p)
|
if (!p)
|
||||||
continue;
|
continue;
|
||||||
temp_commit_buffer = p;
|
to_free = p;
|
||||||
}
|
}
|
||||||
if (!(p = strstr(p, "\n\n")))
|
|
||||||
continue;
|
p = strstr(p, "\n\n");
|
||||||
if (!regexec(®ex, p + 2, 0, NULL, 0)) {
|
matches = p && !regexec(®ex, p + 2, 0, NULL, 0);
|
||||||
|
free(to_free);
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
hashcpy(sha1, commit->object.sha1);
|
hashcpy(sha1, commit->object.sha1);
|
||||||
retval = 0;
|
found = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
regfree(®ex);
|
regfree(®ex);
|
||||||
free(temp_commit_buffer);
|
|
||||||
free_commit_list(list);
|
free_commit_list(list);
|
||||||
for (l = backup; l; l = l->next)
|
for (l = backup; l; l = l->next)
|
||||||
clear_commit_marks(l->item, ONELINE_SEEN);
|
clear_commit_marks(l->item, ONELINE_SEEN);
|
||||||
return retval;
|
free_commit_list(backup);
|
||||||
|
return found ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct grab_nth_branch_switch_cbdata {
|
struct grab_nth_branch_switch_cbdata {
|
||||||
@ -1107,9 +1132,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
struct cache_entry *ce;
|
struct cache_entry *ce;
|
||||||
char *new_path = NULL;
|
char *new_path = NULL;
|
||||||
int pos;
|
int pos;
|
||||||
if (namelen > 2 && name[1] == '/')
|
if (namelen > 2 && name[1] == '/') {
|
||||||
/* don't need mode for commit */
|
struct commit_list *list = NULL;
|
||||||
return get_sha1_oneline(name + 2, sha1);
|
for_each_ref(handle_one_ref, &list);
|
||||||
|
return get_sha1_oneline(name + 2, sha1, list);
|
||||||
|
}
|
||||||
if (namelen < 3 ||
|
if (namelen < 3 ||
|
||||||
name[2] != ':' ||
|
name[2] != ':' ||
|
||||||
name[1] < '0' || '3' < name[1])
|
name[1] < '0' || '3' < name[1])
|
||||||
|
73
t/t1511-rev-parse-caret.sh
Executable file
73
t/t1511-rev-parse-caret.sh
Executable file
@ -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
Block a user