summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/revisions.txt11
-rw-r--r--sha1_name.c20
-rwxr-xr-xt/t1511-rev-parse-caret.sh31
3 files changed, 51 insertions, 11 deletions
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 07961185fe..e0cd03fba9 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -151,11 +151,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
diff --git a/sha1_name.c b/sha1_name.c
index 6d10f052b5..fc5c60d3f2 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -824,8 +824,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 */
@@ -854,12 +858,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))
@@ -879,7 +889,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) {
diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh
index b2f90bea6a..8a5983fcd2 100755
--- a/t/t1511-rev-parse-caret.sh
+++ b/t/t1511-rev-parse-caret.sh
@@ -26,7 +26,10 @@ test_expect_success 'setup' '
git branch expref &&
echo changed >>a-blob &&
git add -u &&
- git commit -m Changed
+ git commit -m Changed &&
+ echo changed-again >>a-blob &&
+ git add -u &&
+ git commit -m Changed-again
'
test_expect_success 'ref^{non-existent}' '
@@ -99,4 +102,30 @@ test_expect_success 'ref^{/!!Exp}' '
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