Skip to content

Commit 36daaac

Browse files
Jehan Binggitster
authored andcommitted
Add a setting to require a filter to be successful
By default, a missing filter driver or a failure from the filter driver is not an error, but merely makes the filter operation a no-op pass through. This is useful to massage the content into a shape that is more convenient for the platform, filesystem, and the user to use, and the content filter mechanism is not used to turn something unusable into usable. However, we could also use of the content filtering mechanism and store the content that cannot be directly used in the repository (e.g. a 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, or decrypt the encrypted content). For such a use case, the content cannot be used when filter driver fails, and we need a way to tell Git to abort the whole operation for such a failing or missing filter driver. Add a new "filter.<driver>.required" configuration variable to mark the second use case. When it is set, git will abort the operation when the filter driver does not exist or exits with a non-zero status code. Signed-off-by: Jehan Bing <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 0364bb1 commit 36daaac

File tree

3 files changed

+92
-14
lines changed

3 files changed

+92
-14
lines changed

Documentation/gitattributes.txt

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -294,16 +294,27 @@ output is used to update the worktree file. Similarly, the
294294
`clean` command is used to convert the contents of worktree file
295295
upon checkin.
296296

297-
A missing filter driver definition in the config is not an error
298-
but makes the filter a no-op passthru.
299-
300-
The content filtering is done to massage the content into a
301-
shape that is more convenient for the platform, filesystem, and
302-
the user to use. The key phrase here is "more convenient" and not
303-
"turning something unusable into usable". In other words, the
304-
intent is that if someone unsets the filter driver definition,
305-
or does not have the appropriate filter program, the project
306-
should still be usable.
297+
One use of the content filtering is to massage the content into a shape
298+
that is more convenient for the platform, filesystem, and the user to use.
299+
For this mode of operation, the key phrase here is "more convenient" and
300+
not "turning something unusable into usable". In other words, the intent
301+
is that if someone unsets the filter driver definition, or does not have
302+
the appropriate filter program, the project should still be usable.
303+
304+
Another use of the content filtering is to store the content that cannot
305+
be directly used in the repository (e.g. a UUID that refers to the true
306+
content stored outside git, or an encrypted content) and turn it into a
307+
usable form upon checkout (e.g. download the external content, or decrypt
308+
the encrypted content).
309+
310+
These two filters behave differently, and by default, a filter is taken as
311+
the former, massaging the contents into more convenient shape. A missing
312+
filter driver definition in the config, or a filter driver that exits with
313+
a non-zero status, is not an error but makes the filter a no-op passthru.
314+
315+
You can declare that a filter turns a content that by itself is unusable
316+
into a usable content by setting the filter.<driver>.required configuration
317+
variable to `true`.
307318

308319
For example, in .gitattributes, you would assign the `filter`
309320
attribute for paths.
@@ -335,6 +346,16 @@ input that is already correctly indented. In this case, the lack of a
335346
smudge filter means that the clean filter _must_ accept its own output
336347
without modifying it.
337348

349+
If a filter _must_ succeed in order to make the stored contents usable,
350+
you can declare that the filter is `required`, in the configuration:
351+
352+
------------------------
353+
[filter "crypt"]
354+
clean = openssl enc ...
355+
smudge = openssl enc -d ...
356+
required
357+
------------------------
358+
338359
Sequence "%f" on the filter command line is replaced with the name of
339360
the file the filter is working on. A filter might use this in keyword
340361
substitution. For example:

convert.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ static struct convert_driver {
429429
struct convert_driver *next;
430430
const char *smudge;
431431
const char *clean;
432+
int required;
432433
} *user_convert, **user_convert_tail;
433434

434435
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)
472473
if (!strcmp("clean", ep))
473474
return git_config_string(&drv->clean, var, value);
474475

476+
if (!strcmp("required", ep)) {
477+
drv->required = git_config_bool(var, value);
478+
return 0;
479+
}
480+
475481
return 0;
476482
}
477483

@@ -747,13 +753,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
747753
{
748754
int ret = 0;
749755
const char *filter = NULL;
756+
int required = 0;
750757
struct conv_attrs ca;
751758

752759
convert_attrs(&ca, path);
753-
if (ca.drv)
760+
if (ca.drv) {
754761
filter = ca.drv->clean;
762+
required = ca.drv->required;
763+
}
755764

756765
ret |= apply_filter(path, src, len, dst, filter);
766+
if (!ret && required)
767+
die("%s: clean filter '%s' failed", path, ca.drv->name);
768+
757769
if (ret) {
758770
src = dst->buf;
759771
len = dst->len;
@@ -771,13 +783,16 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
771783
size_t len, struct strbuf *dst,
772784
int normalizing)
773785
{
774-
int ret = 0;
786+
int ret = 0, ret_filter = 0;
775787
const char *filter = NULL;
788+
int required = 0;
776789
struct conv_attrs ca;
777790

778791
convert_attrs(&ca, path);
779-
if (ca.drv)
792+
if (ca.drv) {
780793
filter = ca.drv->smudge;
794+
required = ca.drv->required;
795+
}
781796

782797
ret |= ident_to_worktree(path, src, len, dst, ca.ident);
783798
if (ret) {
@@ -796,7 +811,12 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
796811
len = dst->len;
797812
}
798813
}
799-
return ret | apply_filter(path, src, len, dst, filter);
814+
815+
ret_filter = apply_filter(path, src, len, dst, filter);
816+
if (!ret_filter && required)
817+
die("%s: smudge filter %s failed", path, ca.drv->name);
818+
819+
return ret | ret_filter;
800820
}
801821

802822
int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)

t/t0021-conversion.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,41 @@ test_expect_success 'filter shell-escaped filenames' '
153153
:
154154
'
155155

156+
test_expect_success 'required filter success' '
157+
git config filter.required.smudge cat &&
158+
git config filter.required.clean cat &&
159+
git config filter.required.required true &&
160+
161+
echo "*.r filter=required" >.gitattributes &&
162+
163+
echo test >test.r &&
164+
git add test.r &&
165+
rm -f test.r &&
166+
git checkout -- test.r
167+
'
168+
169+
test_expect_success 'required filter smudge failure' '
170+
git config filter.failsmudge.smudge false &&
171+
git config filter.failsmudge.clean cat &&
172+
git config filter.failsmudge.required true &&
173+
174+
echo "*.fs filter=failsmudge" >.gitattributes &&
175+
176+
echo test >test.fs &&
177+
git add test.fs &&
178+
rm -f test.fs &&
179+
test_must_fail git checkout -- test.fs
180+
'
181+
182+
test_expect_success 'required filter clean failure' '
183+
git config filter.failclean.smudge cat &&
184+
git config filter.failclean.clean false &&
185+
git config filter.failclean.required true &&
186+
187+
echo "*.fc filter=failclean" >.gitattributes &&
188+
189+
echo test >test.fc &&
190+
test_must_fail git add test.fc
191+
'
192+
156193
test_done

0 commit comments

Comments
 (0)