Skip to content

Commit 524ee67

Browse files
committed
Merge branch 'jb/required-filter'
* jb/required-filter: Add a setting to require a filter to be successful Conflicts: convert.c
2 parents 25a7850 + 36daaac commit 524ee67

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
@@ -452,6 +452,7 @@ static struct convert_driver {
452452
struct convert_driver *next;
453453
const char *smudge;
454454
const char *clean;
455+
int required;
455456
} *user_convert, **user_convert_tail;
456457

457458
static int read_convert_config(const char *var, const char *value, void *cb)
@@ -495,6 +496,11 @@ static int read_convert_config(const char *var, const char *value, void *cb)
495496
if (!strcmp("clean", ep))
496497
return git_config_string(&drv->clean, var, value);
497498

499+
if (!strcmp("required", ep)) {
500+
drv->required = git_config_bool(var, value);
501+
return 0;
502+
}
503+
498504
return 0;
499505
}
500506

@@ -773,13 +779,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
773779
{
774780
int ret = 0;
775781
const char *filter = NULL;
782+
int required = 0;
776783
struct conv_attrs ca;
777784

778785
convert_attrs(&ca, path);
779-
if (ca.drv)
786+
if (ca.drv) {
780787
filter = ca.drv->clean;
788+
required = ca.drv->required;
789+
}
781790

782791
ret |= apply_filter(path, src, len, dst, filter);
792+
if (!ret && required)
793+
die("%s: clean filter '%s' failed", path, ca.drv->name);
794+
783795
if (ret && dst) {
784796
src = dst->buf;
785797
len = dst->len;
@@ -797,13 +809,16 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
797809
size_t len, struct strbuf *dst,
798810
int normalizing)
799811
{
800-
int ret = 0;
812+
int ret = 0, ret_filter = 0;
801813
const char *filter = NULL;
814+
int required = 0;
802815
struct conv_attrs ca;
803816

804817
convert_attrs(&ca, path);
805-
if (ca.drv)
818+
if (ca.drv) {
806819
filter = ca.drv->smudge;
820+
required = ca.drv->required;
821+
}
807822

808823
ret |= ident_to_worktree(path, src, len, dst, ca.ident);
809824
if (ret) {
@@ -822,7 +837,12 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
822837
len = dst->len;
823838
}
824839
}
825-
return ret | apply_filter(path, src, len, dst, filter);
840+
841+
ret_filter = apply_filter(path, src, len, dst, filter);
842+
if (!ret_filter && required)
843+
die("%s: smudge filter %s failed", path, ca.drv->name);
844+
845+
return ret | ret_filter;
826846
}
827847

828848
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)