* [PATCH] Add an option to require a filter to be successful
@ 2012-02-16 23:18 Jehan Bing
2012-02-17 0:03 ` Junio C Hamano
0 siblings, 1 reply; 8+ messages in thread
From: Jehan Bing @ 2012-02-16 23:18 UTC (permalink / raw)
To: git; +Cc: jehan
By default, if a filter driver fails, the unfiltered content will be
used. This patch adds a "filter.<name>.required" config option. When
set to true, git will abort if the filter fails.
A typical usage would be for a "bigfile" filter, where the smudge
command can fail if the file is not available locally. Without the
"required", the content of repository, i.e. a reference to the real
content, will be checked out. Unless one saves the output logs, it
then fairly easy to lose track of what "bigfile" wasn't checked out
correctly.
Another example would be for an encrypted repository if the clean
command (encryption) fails. Without the "required", an unencrypted
content could be stored in the repository by mistake.
Signed-off-by: Jehan Bing <jehan@orb.com>
---
Documentation/gitattributes.txt | 14 ++++++++++++
convert.c | 28 +++++++++++++++++++++---
t/t0021-conversion.sh | 43 +++++++++++++++++++++++++++++++++++++++
3 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index a85b187..4d1af93 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -305,6 +305,10 @@ intent is that if someone unsets the filter driver definition,
or does not have the appropriate filter program, the project
should still be usable.
+The exception is if the filter definition has the `required`
+attribute set to `true`. In that case, the filter must apply
+successfully or git will abort the current operation.
+
For example, in .gitattributes, you would assign the `filter`
attribute for paths.
@@ -335,6 +339,16 @@ input that is already correctly indented. In this case, the lack of a
smudge filter means that the clean filter _must_ accept its own output
without modifying it.
+If you do not wish git to continue if `clean` or `smudge` fail, you can
+add a `required` attribute to the filter:
+
+------------------------
+[filter "crypt"]
+ clean = openssl enc ...
+ smudge = openssl enc -d ...
+ required = true
+------------------------
+
Sequence "%f" on the filter command line is replaced with the name of
the file the filter is working on. A filter might use this in keyword
substitution. For example:
diff --git a/convert.c b/convert.c
index 12868ed..6c95a90 100644
--- a/convert.c
+++ b/convert.c
@@ -429,6 +429,7 @@ static struct convert_driver {
struct convert_driver *next;
const char *smudge;
const char *clean;
+ int required;
} *user_convert, **user_convert_tail;
static int read_convert_config(const char *var, const char *value, void *cb)
@@ -472,6 +473,11 @@ static int read_convert_config(const char *var, const char *value, void *cb)
if (!strcmp("clean", ep))
return git_config_string(&drv->clean, var, value);
+ if (!strcmp("required", ep)) {
+ drv->required = git_config_bool(var, value);
+ return 0;
+ }
+
return 0;
}
@@ -747,13 +753,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
{
int ret = 0;
const char *filter = NULL;
+ int required = 0;
struct conv_attrs ca;
convert_attrs(&ca, path);
- if (ca.drv)
+ if (ca.drv) {
filter = ca.drv->clean;
+ required = ca.drv->required;
+ }
ret |= apply_filter(path, src, len, dst, filter);
+ if (!ret && required)
+ die("required filter '%s' failed", ca.drv->name);
+
if (ret) {
src = dst->buf;
len = dst->len;
@@ -771,13 +783,16 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
size_t len, struct strbuf *dst,
int normalizing)
{
- int ret = 0;
+ int ret = 0, ret_filter = 0;
const char *filter = NULL;
+ int required = 0;
struct conv_attrs ca;
convert_attrs(&ca, path);
- if (ca.drv)
+ if (ca.drv) {
filter = ca.drv->smudge;
+ required = ca.drv->required;
+ }
ret |= ident_to_worktree(path, src, len, dst, ca.ident);
if (ret) {
@@ -796,7 +811,12 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
len = dst->len;
}
}
- return ret | apply_filter(path, src, len, dst, filter);
+
+ ret_filter = apply_filter(path, src, len, dst, filter);
+ if (!ret_filter && required)
+ die("required filter %s failed", ca.drv->name);
+
+ return ret | ret_filter;
}
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index f19e651..f80a59f 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -153,4 +153,47 @@ test_expect_success 'filter shell-escaped filenames' '
:
'
+test_expect_success 'required filter success' '
+ git config filter.required.smudge cat &&
+ git config filter.required.clean cat &&
+ git config filter.required.required true &&
+
+ {
+ echo "*.r filter=required"
+ } >.gitattributes &&
+
+ echo test > test.r &&
+ git add test.r &&
+ rm -f test.r &&
+ git checkout -- test.r
+'
+
+test_expect_success 'required filter smudge failure' '
+ git config filter.failsmudge.smudge false &&
+ git config filter.failsmudge.clean cat &&
+ git config filter.failsmudge.required true &&
+
+ {
+ echo "*.fs filter=failsmudge"
+ } >.gitattributes &&
+
+ echo test > test.fs &&
+ git add test.fs &&
+ rm -f test.fs &&
+ ! git checkout -- test.fs
+'
+
+test_expect_success 'required filter clean failure' '
+ git config filter.failclean.smudge cat &&
+ git config filter.failclean.clean false &&
+ git config filter.failclean.required true &&
+
+ {
+ echo "*.fc filter=failclean"
+ } >.gitattributes &&
+
+ echo test > test.fc &&
+ ! git add test.fc
+'
+
test_done
--
1.7.9
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] Add an option to require a filter to be successful
2012-02-16 23:18 [PATCH] Add an option to require a filter to be successful Jehan Bing
@ 2012-02-17 0:03 ` Junio C Hamano
2012-02-17 1:19 ` [PATCH v2] Add a setting " jehan
0 siblings, 1 reply; 8+ messages in thread
From: Junio C Hamano @ 2012-02-17 0:03 UTC (permalink / raw)
To: Jehan Bing; +Cc: git
Jehan Bing <jehan@orb.com> writes:
> By default, if a filter driver fails, the unfiltered content will be
> used. This patch adds a "filter.<name>.required" config option. When
> set to true, git will abort if the filter fails.
>
> A typical usage would be for a "bigfile" filter, where the smudge
> command can fail if the file is not available locally. Without the
> "required", the content of repository, i.e. a reference to the real
> content, will be checked out. Unless one saves the output logs, it
> then fairly easy to lose track of what "bigfile" wasn't checked out
> correctly.
>
> Another example would be for an encrypted repository if the clean
> command (encryption) fails. Without the "required", an unencrypted
> content could be stored in the repository by mistake.
The above describes sample situations where setting the "required" may be
very useful, without saying anything about in what situation it might be
useful to set it to "optional".
Which makes the reader wonder why this is not done as a bugfix patch that
unconditionally propagates the failure from the filter up the callchain.
That is because the first sentence in the message is too weak. It needs to
be followed by something like:
This is because the content filtering is done to massage the content
into a shape that is more convenient for the platform, filesystem, and
the user to use. The key phrase here is "more convenient" and not
"turning something unusable into usable".
which is what the part of gitattributes documentation shown in the context
says.
That is a statement of principle. And according to that principle, your
configuration option should never exist.
If we are changing that principle and making it configurable, I think the
update to the existing documentation should state things a bit stronger.
We shouldn't be saying "Do not use filter to turn unusable contents to
usable" and in the next breath "But you can use it if you set this at the
same time". That is simply too confusing.
Here is an attempt to rephrase the part that updates the documentation.
Note that filter.<driver>.required is *NOT* an attribute. An attribute is
something you attach to paths.
Documentation/gitattributes.txt | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 25e46ae..39a2654 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -294,16 +294,23 @@ output is used to update the worktree file. Similarly, the
`clean` command is used to convert the contents of worktree file
upon checkin.
-A missing filter driver definition in the config is not an error
-but makes the filter a no-op passthru.
+One use of the content filtering is to massage the content into a shape
+that is more convenient for the platform, filesystem, and the user to use.
-The content filtering is done to massage the content into a
-shape that is more convenient for the platform, filesystem, and
-the user to use. The key phrase here is "more convenient" and not
-"turning something unusable into usable". In other words, the
-intent is that if someone unsets the filter driver definition,
-or does not have the appropriate filter program, the project
-should still be usable.
+Another use of the content filtering is to store the content that cannot
+be directly used in the repository (e.g. an UUID that refers to the true
+content stored outside git, or an encrypted content) and turn it into a
+usable form upon checkout (e.g. download the external content, decrypt the
+encrypted content).
+
+These two filters behave differently, and by default, a filter is taken as
+the former, massaging the contents into more convenient shape. A missing
+filter driver definition in the config, or a filter driver that exits with
+a non-zero status, is not an error but makes the filter a no-op passthru.
+
+You can declare that a filter turns a content that by itself is unusable
+into usable by setting filter.<drivername>.required configuration variable
+to `true`.
For example, in .gitattributes, you would assign the `filter`
attribute for paths.
@@ -335,6 +342,16 @@ input that is already correctly indented. In this case, the lack of a
smudge filter means that the clean filter _must_ accept its own output
without modifying it.
+If a filter _must_ succeed in order to make the stored contents usable,
+you can declare that the filter is `required`, in the configuration:
+
+------------------------
+[filter "crypt"]
+ clean = openssl enc ...
+ smudge = openssl enc -d ...
+ required
+------------------------
+
Sequence "%f" on the filter command line is replaced with the name of
the file the filter is working on. A filter might use this in keyword
substitution. For example:
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v2] Add a setting to require a filter to be successful
2012-02-17 0:03 ` Junio C Hamano
@ 2012-02-17 1:19 ` jehan
2012-02-17 7:08 ` Johannes Sixt
0 siblings, 1 reply; 8+ messages in thread
From: jehan @ 2012-02-17 1:19 UTC (permalink / raw)
To: gitster; +Cc: git, jehan
From: Jehan Bing <jehan@orb.com>
By default, if a filter driver fails, the unfiltered content will be
used. This is because the content filtering is done to massage the
content into a shape that is more convenient for the platform,
filesystem, and the user to use. The key phrase here is "more
convenient" and not "turning something unusable into usable".
However, another use of the content filtering is to store the content
that cannot be directly used in the repository (e.g. an UUID that
refers to the true content stored outside git, or an encrypted
content) and turn it into a usable form upon checkout (e.g. download
the external content, decrypt the encrypted content).
In this situation, it is preferable to have git fail instead of using
the unfiltered content.
This patch adds an optional "filter.<filtername>.required"
configuration variable. When missing or set to false, git will use
the unfiltered content if the filter driver fails (old behavior).
When set to true, git will instead abort the current operation.
Signed-off-by: Jehan Bing <jehan@orb.com>
---
Thanks Junio for your comment. This version use your version of
gitattributes.txt and I rewrote the commit message to be
stronger.
-Jehan
Documentation/gitattributes.txt | 35 +++++++++++++++++++++++--------
convert.c | 28 +++++++++++++++++++++---
t/t0021-conversion.sh | 43 +++++++++++++++++++++++++++++++++++++++
3 files changed, 93 insertions(+), 13 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index a85b187..6abaf9a 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -294,16 +294,23 @@ output is used to update the worktree file. Similarly, the
`clean` command is used to convert the contents of worktree file
upon checkin.
-A missing filter driver definition in the config is not an error
-but makes the filter a no-op passthru.
+One use of the content filtering is to massage the content into a shape
+that is more convenient for the platform, filesystem, and the user to use.
-The content filtering is done to massage the content into a
-shape that is more convenient for the platform, filesystem, and
-the user to use. The key phrase here is "more convenient" and not
-"turning something unusable into usable". In other words, the
-intent is that if someone unsets the filter driver definition,
-or does not have the appropriate filter program, the project
-should still be usable.
+Another use of the content filtering is to store the content that cannot
+be directly used in the repository (e.g. an UUID that refers to the true
+content stored outside git, or an encrypted content) and turn it into a
+usable form upon checkout (e.g. download the external content, decrypt the
+encrypted content).
+
+These two filters behave differently, and by default, a filter is taken as
+the former, massaging the contents into more convenient shape. A missing
+filter driver definition in the config, or a filter driver that exits with
+a non-zero status, is not an error but makes the filter a no-op passthru.
+
+You can declare that a filter turns a content that by itself is unusable
+into usable by setting filter.<drivername>.required configuration variable
+to `true`.
For example, in .gitattributes, you would assign the `filter`
attribute for paths.
@@ -335,6 +342,16 @@ input that is already correctly indented. In this case, the lack of a
smudge filter means that the clean filter _must_ accept its own output
without modifying it.
+If a filter _must_ succeed in order to make the stored contents usable,
+you can declare that the filter is `required`, in the configuration:
+
+------------------------
+[filter "crypt"]
+ clean = openssl enc ...
+ smudge = openssl enc -d ...
+ required
+------------------------
+
Sequence "%f" on the filter command line is replaced with the name of
the file the filter is working on. A filter might use this in keyword
substitution. For example:
diff --git a/convert.c b/convert.c
index 12868ed..6c95a90 100644
--- a/convert.c
+++ b/convert.c
@@ -429,6 +429,7 @@ static struct convert_driver {
struct convert_driver *next;
const char *smudge;
const char *clean;
+ int required;
} *user_convert, **user_convert_tail;
static int read_convert_config(const char *var, const char *value, void *cb)
@@ -472,6 +473,11 @@ static int read_convert_config(const char *var, const char *value, void *cb)
if (!strcmp("clean", ep))
return git_config_string(&drv->clean, var, value);
+ if (!strcmp("required", ep)) {
+ drv->required = git_config_bool(var, value);
+ return 0;
+ }
+
return 0;
}
@@ -747,13 +753,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
{
int ret = 0;
const char *filter = NULL;
+ int required = 0;
struct conv_attrs ca;
convert_attrs(&ca, path);
- if (ca.drv)
+ if (ca.drv) {
filter = ca.drv->clean;
+ required = ca.drv->required;
+ }
ret |= apply_filter(path, src, len, dst, filter);
+ if (!ret && required)
+ die("required filter '%s' failed", ca.drv->name);
+
if (ret) {
src = dst->buf;
len = dst->len;
@@ -771,13 +783,16 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
size_t len, struct strbuf *dst,
int normalizing)
{
- int ret = 0;
+ int ret = 0, ret_filter = 0;
const char *filter = NULL;
+ int required = 0;
struct conv_attrs ca;
convert_attrs(&ca, path);
- if (ca.drv)
+ if (ca.drv) {
filter = ca.drv->smudge;
+ required = ca.drv->required;
+ }
ret |= ident_to_worktree(path, src, len, dst, ca.ident);
if (ret) {
@@ -796,7 +811,12 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
len = dst->len;
}
}
- return ret | apply_filter(path, src, len, dst, filter);
+
+ ret_filter = apply_filter(path, src, len, dst, filter);
+ if (!ret_filter && required)
+ die("required filter %s failed", ca.drv->name);
+
+ return ret | ret_filter;
}
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index f19e651..f80a59f 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -153,4 +153,47 @@ test_expect_success 'filter shell-escaped filenames' '
:
'
+test_expect_success 'required filter success' '
+ git config filter.required.smudge cat &&
+ git config filter.required.clean cat &&
+ git config filter.required.required true &&
+
+ {
+ echo "*.r filter=required"
+ } >.gitattributes &&
+
+ echo test > test.r &&
+ git add test.r &&
+ rm -f test.r &&
+ git checkout -- test.r
+'
+
+test_expect_success 'required filter smudge failure' '
+ git config filter.failsmudge.smudge false &&
+ git config filter.failsmudge.clean cat &&
+ git config filter.failsmudge.required true &&
+
+ {
+ echo "*.fs filter=failsmudge"
+ } >.gitattributes &&
+
+ echo test > test.fs &&
+ git add test.fs &&
+ rm -f test.fs &&
+ ! git checkout -- test.fs
+'
+
+test_expect_success 'required filter clean failure' '
+ git config filter.failclean.smudge cat &&
+ git config filter.failclean.clean false &&
+ git config filter.failclean.required true &&
+
+ {
+ echo "*.fc filter=failclean"
+ } >.gitattributes &&
+
+ echo test > test.fc &&
+ ! git add test.fc
+'
+
test_done
--
1.7.9
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Add a setting to require a filter to be successful
2012-02-17 1:19 ` [PATCH v2] Add a setting " jehan
@ 2012-02-17 7:08 ` Johannes Sixt
2012-02-17 15:35 ` Junio C Hamano
2012-02-18 0:07 ` Junio C Hamano
0 siblings, 2 replies; 8+ messages in thread
From: Johannes Sixt @ 2012-02-17 7:08 UTC (permalink / raw)
To: jehan; +Cc: gitster, git
Am 2/17/2012 2:19, schrieb jehan@orb.com:
> @@ -747,13 +753,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
...
> ret |= apply_filter(path, src, len, dst, filter);
> + if (!ret && required)
> + die("required filter '%s' failed", ca.drv->name);
Wouldn't it be much more helpful if this were:
die("%s: clean filter '%s' failed", path, ca.drv->name);
Likewise (with s/clean/smudge/) in convert_to_working_tree_internal().
> + ! git checkout -- test.fs
test_must_fail git checkout -- test.fs
> + ! git add test.fc
test_must_fail git add test.fc
-- Hannes
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Add a setting to require a filter to be successful
2012-02-17 7:08 ` Johannes Sixt
@ 2012-02-17 15:35 ` Junio C Hamano
2012-02-18 0:07 ` Junio C Hamano
1 sibling, 0 replies; 8+ messages in thread
From: Junio C Hamano @ 2012-02-17 15:35 UTC (permalink / raw)
To: Johannes Sixt; +Cc: jehan, gitster, git
Johannes Sixt <j.sixt@viscovery.net> writes:
> Am 2/17/2012 2:19, schrieb jehan@orb.com:
>> @@ -747,13 +753,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
> ...
>> ret |= apply_filter(path, src, len, dst, filter);
>> + if (!ret && required)
>> + die("required filter '%s' failed", ca.drv->name);
>
> Wouldn't it be much more helpful if this were:
>
> die("%s: clean filter '%s' failed", path, ca.drv->name);
>
> Likewise (with s/clean/smudge/) in convert_to_working_tree_internal().
>
>> + ! git checkout -- test.fs
>
> test_must_fail git checkout -- test.fs
>
>> + ! git add test.fc
>
> test_must_fail git add test.fc
>
> -- Hannes
Thanks; I'll just squash these in in-place.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Add a setting to require a filter to be successful
2012-02-17 7:08 ` Johannes Sixt
2012-02-17 15:35 ` Junio C Hamano
@ 2012-02-18 0:07 ` Junio C Hamano
2012-02-18 0:43 ` Jehan Bing
1 sibling, 1 reply; 8+ messages in thread
From: Junio C Hamano @ 2012-02-18 0:07 UTC (permalink / raw)
To: jehan; +Cc: git, Johannes Sixt
A few test in t0021 use 'false' as the filter, which can exit without
reading any byte from us, before we start writing and causes us to die
with SIGPIPE, leading to intermittent test failure. I think treating this
as a failure of running the filter (the end user's filter should read what
is fed in full, produce its output and write the result back to us) is the
right thing to do, and this patch needs more work to handle such a
situation better, probably by using sigchain_push(SIGPIPE) or something.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Add a setting to require a filter to be successful
2012-02-18 0:07 ` Junio C Hamano
@ 2012-02-18 0:43 ` Jehan Bing
2012-02-18 7:27 ` Junio C Hamano
0 siblings, 1 reply; 8+ messages in thread
From: Jehan Bing @ 2012-02-18 0:43 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Johannes Sixt
On 2012-02-17 16:07, Junio C Hamano wrote:
> A few test in t0021 use 'false' as the filter, which can exit without
> reading any byte from us, before we start writing and causes us to die
> with SIGPIPE, leading to intermittent test failure. I think treating this
> as a failure of running the filter (the end user's filter should read what
> is fed in full, produce its output and write the result back to us) is the
> right thing to do, and this patch needs more work to handle such a
> situation better, probably by using sigchain_push(SIGPIPE) or something.
If I understand what you're saying, current version of git already have
the problem: if a filter fails without reading anything, git will die
instead of using the unfiltered content. My patch has only made the
issue apparent by testing with a failing filter.
Am I understanding correctly?
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Add a setting to require a filter to be successful
2012-02-18 0:43 ` Jehan Bing
@ 2012-02-18 7:27 ` Junio C Hamano
0 siblings, 0 replies; 8+ messages in thread
From: Junio C Hamano @ 2012-02-18 7:27 UTC (permalink / raw)
To: Jehan Bing; +Cc: git, Johannes Sixt
Jehan Bing <jehan@orb.com> writes:
> If I understand what you're saying, current version of git already
> have the problem: if a filter fails without reading anything, git will
> die instead of using the unfiltered content. My patch has only made
> the issue apparent by testing with a failing filter.
> Am I understanding correctly?
Yes.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2012-02-18 7:28 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-02-16 23:18 [PATCH] Add an option to require a filter to be successful Jehan Bing
2012-02-17 0:03 ` Junio C Hamano
2012-02-17 1:19 ` [PATCH v2] Add a setting " jehan
2012-02-17 7:08 ` Johannes Sixt
2012-02-17 15:35 ` Junio C Hamano
2012-02-18 0:07 ` Junio C Hamano
2012-02-18 0:43 ` Jehan Bing
2012-02-18 7:27 ` Junio C Hamano
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).