From: Will Palmer <wmpalmer@gmail.com>
To: git@vger.kernel.org
Cc: Will Palmer <wmpalmer@gmail.com>
Subject: [PATCH 2/2] object name: introduce '^{/!<negative pattern>}' notation
Date: Wed, 3 Jun 2015 21:54:14 +0100 [thread overview]
Message-ID: <1433364854-30088-3-git-send-email-wmpalmer@gmail.com> (raw)
In-Reply-To: <1433364854-30088-1-git-send-email-wmpalmer@gmail.com>
To name a commit, you can now say
$ git rev-parse HEAD^{/!foo}
and it will return the hash of the first commit reachable from HEAD,
whose commit message does not contain "foo".
Since the ability to reference a commit by "name" was introduced (way
back in 1.5, in 364d3e6), with the across-all-refs syntax of ':/foo',
there has been a note in the documentation indicating that a leading
exclamation mark was "reserved for now" (unless followed immediately be
another exclamation mark.)
At the time, this was sensible: we didn't get the '^{/foo}' flavour
until sometime around 1.7.4 (41cd797) , so while a "negative search" was
a foreseeable feature, it wouldn't have made much sense to apply one
across all refs, as the result would have been essentially random.
These days, a negative pattern can make sense. In particular, if you tend
to use a rebase-heavy workflow with many "work in progress" commits, it
may be useful to diff or rebase against the latest "not work-in-progress"
commit. That sort of thing now possible, via commands such as:
$ git rebase -i @^{/!^WIP}
Perhaps notably, the "special case" for the empty pattern has been
extended to handle the empty negative pattern - which never matches, to
continue to ensure that an empty pattern never reaches the real regexp
code, as per notes in 4322842 "get_sha1: handle special case $commit^{/}"
Signed-off-by: Will Palmer <wmpalmer@gmail.com>
---
Documentation/revisions.txt | 7 ++++---
sha1_name.c | 22 ++++++++++++++++------
t/t1511-rev-parse-caret.sh | 32 +++++++++++++++++++++++++++++---
3 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 0796118..6a6b8b9 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -151,9 +151,10 @@ 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.
+ reachable from any ref. To name a commit whose commit message does not
+ match the specified regular expression, begin the pattern-part with a
+ '!', e.g. ':/!foo'. If the commit message you wish to match starts with
+ a '!' you have to repeat that.
The regular expression can match any part of the commit message. To
match messages starting with a string, one can use e.g. ':/^foo'.
diff --git a/sha1_name.c b/sha1_name.c
index 46218ba..3d50dc9 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -737,11 +737,15 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
/*
* $commit^{/}. Some regex implementation may reject.
- * We don't need regex anyway. '' pattern always matches.
+ * We don't need regex anyway. '' pattern always matches,
+ * and '!' pattern never matches.
*/
if (sp[1] == '}')
return 0;
+ if (sp[1] == '!' && sp[2] == '}')
+ return -1;
+
prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
commit_list_insert((struct commit *)o, &list);
ret = get_sha1_oneline(prefix, sha1, list);
@@ -825,8 +829,9 @@ 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 a '!', like:
+ * ':/!WIP'. If you want to match a message beginning with a literal
+ * '!', you heave to repeat the exlamation mark.
*/
/* Remember to update object flag allocation in object.h */
@@ -855,11 +860,16 @@ 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);
+ if (prefix[1] != '!') {
+ negative = 1;
+ } else if (prefix[1] == '!' && prefix[2] == '!') {
+ negative = 1;
+ prefix++;
+ }
prefix++;
}
@@ -880,7 +890,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(®ex, p + 2, 0, NULL, 0);
+ matches = p && (negative ^ !regexec(®ex, 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 0c46e5c..1d27aca 100755
--- a/t/t1511-rev-parse-caret.sh
+++ b/t/t1511-rev-parse-caret.sh
@@ -19,13 +19,17 @@ test_expect_success 'setup' '
echo modified >>a-blob &&
git add -u &&
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
+ git commit -m Changed &&
+ echo changed-again >>a-blob &&
+ git add -u &&
+ git commit -m Changed-again
'
test_expect_success 'ref^{non-existent}' '
@@ -84,8 +88,8 @@ 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}' '
@@ -94,4 +98,26 @@ test_expect_success 'ref^{/!!Exp}' '
test_cmp expected actual
'
+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
--
2.3.0.rc1
prev parent reply other threads:[~2015-06-03 20:54 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-06-03 20:54 [PATCH 0/2] specify commit by negative pattern Will Palmer
2015-06-03 20:54 ` [PATCH 1/2] test for '!' handling in rev-parse's named commits Will Palmer
2015-06-03 21:52 ` Junio C Hamano
2015-06-03 22:44 ` Will Palmer
2015-06-04 17:09 ` Junio C Hamano
2015-06-03 23:12 ` Junio C Hamano
2015-06-03 20:54 ` Will Palmer [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1433364854-30088-3-git-send-email-wmpalmer@gmail.com \
--to=wmpalmer@gmail.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).