Merge branch 'wp/sha1-name-negative-match'

A new "<branch>^{/!-<pattern>}" notation can be used to name a
commit that is reachable from <branch> that does not match the
given <pattern>.

* wp/sha1-name-negative-match:
  object name: introduce '^{/!-<negative pattern>}' notation
  test for '!' handling in rev-parse's named commits
This commit is contained in:
Junio C Hamano 2016-02-10 14:20:10 -08:00
commit fb795323ce
3 changed files with 73 additions and 11 deletions

View file

@ -176,11 +176,12 @@ existing tag object.
A colon, followed by a slash, followed by a text, names
a commit whose commit message matches the specified regular expression.
This name returns the youngest matching commit which is
reachable from any ref. If the commit message starts with a
'!' you have to repeat that; the special sequence ':/!',
followed by something else than '!', is reserved for now.
The regular expression can match any part of the commit message. To
match messages starting with a string, one can use e.g. ':/^foo'.
reachable from any ref. The regular expression can match any part of the
commit message. To match messages starting with a string, one can use
e.g. ':/^foo'. The special sequence ':/!' is reserved for modifiers to what
is matched. ':/!-foo' performs a negative match, while ':/!!foo' matches a
literal '!' character, followed by 'foo'. Any other sequence beginning with
':/!' is reserved for now.
'<rev>:<path>', e.g. 'HEAD:README', ':README', 'master:./README'::
A suffix ':' followed by a path names the blob or tree

View file

@ -848,8 +848,12 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
* through history and returning the first commit whose message starts
* the given regular expression.
*
* For future extension, ':/!' is reserved. If you want to match a message
* beginning with a '!', you have to repeat the exclamation mark.
* For negative-matching, prefix the pattern-part with '!-', like: ':/!-WIP'.
*
* For a literal '!' character at the beginning of a pattern, you have to repeat
* that, like: ':/!!foo'
*
* For future extension, all other sequences beginning with ':/!' are reserved.
*/
/* Remember to update object flag allocation in object.h */
@ -878,12 +882,18 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
{
struct commit_list *backup = NULL, *l;
int found = 0;
int negative = 0;
regex_t regex;
if (prefix[0] == '!') {
if (prefix[1] != '!')
die ("Invalid search pattern: %s", prefix);
prefix++;
if (prefix[0] == '-') {
prefix++;
negative = 1;
} else if (prefix[0] != '!') {
die ("Invalid search pattern: %s", prefix);
}
}
if (regcomp(&regex, prefix, REG_EXTENDED))
@ -903,7 +913,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
continue;
buf = get_commit_buffer(commit, NULL);
p = strstr(buf, "\n\n");
matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
matches = negative ^ (p && !regexec(&regex, p + 2, 0, NULL, 0));
unuse_commit_buffer(commit, buf);
if (matches) {

View file

@ -18,7 +18,18 @@ test_expect_success 'setup' '
git checkout master &&
echo modified >>a-blob &&
git add -u &&
git commit -m Modified
git commit -m Modified &&
git branch modref &&
echo changed! >>a-blob &&
git add -u &&
git commit -m !Exp &&
git branch expref &&
echo changed >>a-blob &&
git add -u &&
git commit -m Changed &&
echo changed-again >>a-blob &&
git add -u &&
git commit -m Changed-again
'
test_expect_success 'ref^{non-existent}' '
@ -77,4 +88,44 @@ test_expect_success 'ref^{/Initial}' '
test_cmp expected actual
'
test_expect_success 'ref^{/!Exp}' '
test_must_fail git rev-parse master^{/!Exp}
'
test_expect_success 'ref^{/!}' '
test_must_fail git rev-parse master^{/!}
'
test_expect_success 'ref^{/!!Exp}' '
git rev-parse expref >expected &&
git rev-parse master^{/!!Exp} >actual &&
test_cmp expected actual
'
test_expect_success 'ref^{/!-}' '
test_must_fail git rev-parse master^{/!-}
'
test_expect_success 'ref^{/!-.}' '
test_must_fail git rev-parse master^{/!-.}
'
test_expect_success 'ref^{/!-non-existent}' '
git rev-parse master >expected &&
git rev-parse master^{/!-non-existent} >actual &&
test_cmp expected actual
'
test_expect_success 'ref^{/!-Changed}' '
git rev-parse expref >expected &&
git rev-parse master^{/!-Changed} >actual &&
test_cmp expected actual
'
test_expect_success 'ref^{/!-!Exp}' '
git rev-parse modref >expected &&
git rev-parse expref^{/!-!Exp} >actual &&
test_cmp expected actual
'
test_done