diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index f85ccff..ad9b243 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -95,8 +95,9 @@ refname::
abbreviation mode. If `lstrip=<N>` is appended, strips `<N>`
slash-separated path components from the front of the refname
(e.g., `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo`.
- `<N>` must be a positive integer. If a displayed ref has fewer
- components than `<N>`, the command aborts with an error.
+ if `<N>` is a negative number, then only `<N>` path components
+ are left behind. If a displayed ref has fewer components than
+ `<N>`, the command aborts with an error.
objecttype::
The type of the object (`blob`, `tree`, `commit`, `tag`).
diff --git a/ref-filter.c b/ref-filter.c
index c74b08d..deb2b29 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -33,7 +33,7 @@ struct if_then_else {
struct refname_atom {
enum { R_NORMAL, R_SHORT, R_LSTRIP } option;
- unsigned int lstrip;
+ int lstrip;
};
/*@@ -92,8 +92,8 @@ static void refname_atom_parser_internal(struct refname_atom *atom,
atom->option = R_SHORT;
else if (skip_prefix(arg, "lstrip=", &arg)) {
atom->option = R_LSTRIP;
- if (strtoul_ui(arg, 10, &atom->lstrip) || atom->lstrip <= 0)
- die(_("positive value expected refname:lstrip=%s"), arg);
+ if (strtol_i(arg, 10, &atom->lstrip))
+ die(_("Integer value expected refname:lstrip=%s"), arg);
} else
die(_("unrecognized %%(%s) argument: %s"), name, arg);
}@@ -1071,21 +1071,37 @@ static inline char *copy_advance(char *dst, const char *src)
return dst;
}
-static const char *lstrip_ref_components(const char *refname, unsigned int len)
+static const char *lstrip_ref_components(const char *refname, int len)
{
long remaining = len;
const char *start = refname;
+ if (len < 0) {
+ int i;
+ const char *p = refname;
+
+ /* Find total no of '/' separated path-components */
+ for (i = 0; p[i]; p[i] == '/' ? i++ : *p++);
+ /*
+ * The number of components we need to strip is now
+ * the total minus the components to be left (Plus one
+ * because we count the number of '/', but the number
+ * of components is one more than the no of '/').
+ */
+ remaining = i + len + 1;
+ }
+
while (remaining) {
switch (*start++) {
case '\0':
- die(_("ref '%s' does not have %ud components to :lstrip"),
+ die(_("ref '%s' does not have %d components to :lstrip"),
refname, len);
case '/':
remaining--;
break;
}
}
+
return start;
}
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index 2b1af9c..26adca8 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -53,12 +53,16 @@ test_atom head refname refs/heads/master
test_atom head refname:short master
test_atom head refname:lstrip=1 heads/master
test_atom head refname:lstrip=2 master
+test_atom head refname:lstrip=-1 master
+test_atom head refname:lstrip=-2 heads/master
test_atom head upstream refs/remotes/origin/master
test_atom head upstream:short origin/master
test_atom head upstream:lstrip=2 origin/master
+test_atom head upstream:lstrip=-2 origin/master
test_atom head push refs/remotes/myfork/master
test_atom head push:short myfork/master
test_atom head push:lstrip=1 remotes/myfork/master
+test_atom head push:lstrip=-1 master
test_atom head objecttype commit
test_atom head objectsize 171
test_atom head objectname $(git rev-parse refs/heads/master)
@@ -141,14 +145,9 @@ test_expect_success 'Check invalid atoms names are errors' '
test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
'
-test_expect_success 'arguments to :lstrip must be positive integers' '
- test_must_fail git for-each-ref --format="%(refname:lstrip=0)" &&
- test_must_fail git for-each-ref --format="%(refname:lstrip=-1)" &&
- test_must_fail git for-each-ref --format="%(refname:lstrip=foo)"
-'
-
test_expect_success 'stripping refnames too far gives an error' '
- test_must_fail git for-each-ref --format="%(refname:lstrip=3)"
+ test_must_fail git for-each-ref --format="%(refname:lstrip=3)" &&
+ test_must_fail git for-each-ref --format="%(refname:lstrip=-4)"
'
test_expect_success 'Check format specifiers are ignored in naming date atoms' '
@@ -603,10 +602,12 @@ test_expect_success 'Verify usage of %(symref:short) atom' '
cat >expected <<EOF
master
+heads/master
EOF
test_expect_success 'Verify usage of %(symref:lstrip) atom' '
git for-each-ref --format="%(symref:lstrip=2)" refs/heads/sym > actual &&
+ git for-each-ref --format="%(symref:lstrip=-2)" refs/heads/sym >> actual &&
test_cmp expected actual
'
--
2.10.2