* [RFC PATCH] builtin-apply: prevent non-explicit permission changes
@ 2008-12-30 23:53 Alexander Potashev
2009-01-01 13:00 ` Junio C Hamano
2009-01-02 10:55 ` Junio C Hamano
0 siblings, 2 replies; 7+ messages in thread
From: Alexander Potashev @ 2008-12-30 23:53 UTC (permalink / raw)
To: Git Mailing List
Prevent 'git apply' from changing permissions without
'old mode'/'new mode' lines in patch.
(WARNING: this changes the behaviour of 'git apply')
Signed-off-by: Alexander Potashev <aspotashev@gmail.com>
---
Once upon a time there was a shell script in a Git repository. But that
shell script had 100644 permission (regular file). Then I did
'chmod +x', commit... but the shell script was related to my friend's
stuff in the repository and I received a patch from him regarding the
script. But the patch was against a repository version before
'chmod +x', thus it contained an index line such as the following:
index fc3c3a4..066a4ac 100644
(it still had '100644' permissions)
I have to note that there was no 'old/new mode' lines. But when I ran
'git am <patch>' it restored '100644' permissions. So, 'git am' changed
my permissions (100755 -> 100644) without any explicit permission
changes in the patch.
I think, 'git apply'/'git am' should apply only _changes_ _mentioned_ in
patch; if there's no 'old mode ...'/'new mode ...' lines in it, 'git
apply' shouldn't change the permissions.
Test cases are probably wanted, but I don't really know how to do them
and I'll only give a chain of commands to reproduce the issue:
mkdir repo
cd repo
git init
echo "This is a shell script" > script.sh
git add script.sh
git ci -m "initial commit"
echo "a new line and a newline" >> script.sh
git ci -a -m "only content changes" # aka patch to apply
git format-patch -1 # now we have a patch
git reset --hard HEAD^
chmod +x script.sh
git ci -a -m "permission changes"
git am 0001-only-content-changes.patch
stat -c %a script.sh # check the result
'stat' says '644' if 'git am' has changed the permissions or '755' if
it hasn't.
Alexander
builtin-apply.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/builtin-apply.c b/builtin-apply.c
index 07244b0..071f6d8 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -630,7 +630,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
memcpy(patch->new_sha1_prefix, line, len);
patch->new_sha1_prefix[len] = 0;
if (*ptr == ' ')
- patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+ patch->old_mode = strtoul(ptr+1, NULL, 8);
return 0;
}
@@ -2447,6 +2447,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st_mode, patch->old_mode);
+ patch->new_mode = st_mode;
return 0;
is_new:
--
1.6.0.6
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC PATCH] builtin-apply: prevent non-explicit permission changes
2008-12-30 23:53 [RFC PATCH] builtin-apply: prevent non-explicit permission changes Alexander Potashev
@ 2009-01-01 13:00 ` Junio C Hamano
2009-01-01 22:17 ` Alexander Potashev
2009-01-02 10:55 ` Junio C Hamano
1 sibling, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2009-01-01 13:00 UTC (permalink / raw)
To: Alexander Potashev; +Cc: Git Mailing List
Alexander Potashev <aspotashev@gmail.com> writes:
> builtin-apply.c | 3 ++-
> 1 files changed, 2 insertions(+), 1 deletions(-)
>
> diff --git a/builtin-apply.c b/builtin-apply.c
> index 07244b0..071f6d8 100644
> --- a/builtin-apply.c
> +++ b/builtin-apply.c
> @@ -630,7 +630,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
> memcpy(patch->new_sha1_prefix, line, len);
> patch->new_sha1_prefix[len] = 0;
> if (*ptr == ' ')
> - patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
> + patch->old_mode = strtoul(ptr+1, NULL, 8);
> return 0;
> }
>
> @@ -2447,6 +2447,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
> if (st_mode != patch->old_mode)
> fprintf(stderr, "warning: %s has type %o, expected %o\n",
> old_name, st_mode, patch->old_mode);
> + patch->new_mode = st_mode;
Can you do this unconditionally, overwriting whatever we read from the
patch header metainfo lines?
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH] builtin-apply: prevent non-explicit permission changes
2009-01-01 13:00 ` Junio C Hamano
@ 2009-01-01 22:17 ` Alexander Potashev
2009-01-02 0:56 ` Junio C Hamano
0 siblings, 1 reply; 7+ messages in thread
From: Alexander Potashev @ 2009-01-01 22:17 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
On 05:00 Thu 01 Jan , Junio C Hamano wrote:
> Alexander Potashev <aspotashev@gmail.com> writes:
>
> > builtin-apply.c | 3 ++-
> > 1 files changed, 2 insertions(+), 1 deletions(-)
> >
> > diff --git a/builtin-apply.c b/builtin-apply.c
> > index 07244b0..071f6d8 100644
> > --- a/builtin-apply.c
> > +++ b/builtin-apply.c
> > @@ -630,7 +630,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
> > memcpy(patch->new_sha1_prefix, line, len);
> > patch->new_sha1_prefix[len] = 0;
> > if (*ptr == ' ')
> > - patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
> > + patch->old_mode = strtoul(ptr+1, NULL, 8);
> > return 0;
> > }
> >
> > @@ -2447,6 +2447,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
> > if (st_mode != patch->old_mode)
> > fprintf(stderr, "warning: %s has type %o, expected %o\n",
> > old_name, st_mode, patch->old_mode);
> > + patch->new_mode = st_mode;
>
> Can you do this unconditionally, overwriting whatever we read from the
> patch header metainfo lines?
Do you mean overwriting of 'patch->new_mode' right after patch parsing?
If so, there would be yet another call to 'stat' to get the permissions
of the existing file (that is not very good).
I'm not very familiar with Git sources.
Also, I don't understand what are the permissions in 'index ...' lines
for (e.g. "index fc3c3a4..066a4ac 100644"), my patch simply drops them:
> > - patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
> > + patch->old_mode = strtoul(ptr+1, NULL, 8);
...not completely drops, probably we should cross out this line
completely (I don't know whether it breaks something).
Alexander
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH] builtin-apply: prevent non-explicit permission changes
2009-01-01 22:17 ` Alexander Potashev
@ 2009-01-02 0:56 ` Junio C Hamano
2009-01-02 13:37 ` Alexander Potashev
0 siblings, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2009-01-02 0:56 UTC (permalink / raw)
To: Alexander Potashev; +Cc: Git Mailing List
Alexander Potashev <aspotashev@gmail.com> writes:
> On 05:00 Thu 01 Jan , Junio C Hamano wrote:
>> Alexander Potashev <aspotashev@gmail.com> writes:
> ...
>> > @@ -2447,6 +2447,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
>> > if (st_mode != patch->old_mode)
>> > fprintf(stderr, "warning: %s has type %o, expected %o\n",
>> > old_name, st_mode, patch->old_mode);
>> > + patch->new_mode = st_mode;
>>
>> Can you do this unconditionally, overwriting whatever we read from the
>> patch header metainfo lines?
>
> Do you mean overwriting of 'patch->new_mode' right after patch parsing?
My question was if we should assign st_mode to new_mode _unconditionally_
here, even when patch->new_mode has already been read from the explicit
mode change line (i.e. "new mode ", line not "index "line) of the patch
input.
The call-chain of the program looks like this:
-> apply_patch()
-> parse_chunk()
-> find_header()
* initialize new_mode and old_mode to 0
-> parse_git_header()
* set new_mode and old_mode from the patch metainfo, i.e.
"new mode", "old mode" and "index" lines.
-> parse_single_patch()
-> check_patch_list()
-> check_patch()
-> check_preimage()
* make sure there is no local mods
* warn if old_mode read from the patch (i.e. the preimage file
the patch submitter used to prepare the patch against) does not
match what we have
* warn about mode inconsistency (e.g. the patch submitter thinks
the mode should be 0644 but our tree has 0755).
-> apply_data()
-> write_out_results()
-> write_out_one_result(0)
* delete old
-> write_out_one_result(1)
* create new
Currently the mode 100644 on the "index" line in a patch is handled
exactly in the same way as having "old mode 100644" and "new mode 100644"
lines in the metainfo. The patch submitter claims to have started from
100644 and he claims that he wants to have 100644 as the result. That is
why there is a warning in check_patch().
If we stop reading the new mode from the "index" line (but we still read
"old_mode" there) without any other change you made in your patch, what
breaks (i.e. without the patch->new_mode assignment hunk)? I haven't
followed the codepath too closely, and I suspect you found some cases
where new_mode stays 0 as initialized, and that may be the reason you have
this assignment.
But the assignment being unconditional bothered me a lot.
I tend to agree that the current "The final mode bits I want to have on
this path is this" semantics we give to the "index" line is much less
useful and less sane and it is a good idea to redefine it as "FYI, the
copy I made this patch against had this mode bits. I do not intend to
change the mode bits of the path with this patch."
builtin-apply.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git c/builtin-apply.c w/builtin-apply.c
index 07244b0..a8f75ed 100644
--- c/builtin-apply.c
+++ w/builtin-apply.c
@@ -630,7 +630,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
memcpy(patch->new_sha1_prefix, line, len);
patch->new_sha1_prefix[len] = 0;
if (*ptr == ' ')
- patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+ patch->old_mode = strtoul(ptr+1, NULL, 8);
return 0;
}
@@ -2447,6 +2447,8 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st_mode, patch->old_mode);
+ if (!patch->new_mode)
+ patch->new_mode = st_mode;
return 0;
is_new:
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC PATCH] builtin-apply: prevent non-explicit permission changes
2008-12-30 23:53 [RFC PATCH] builtin-apply: prevent non-explicit permission changes Alexander Potashev
2009-01-01 13:00 ` Junio C Hamano
@ 2009-01-02 10:55 ` Junio C Hamano
2009-01-02 17:35 ` Jeff King
1 sibling, 1 reply; 7+ messages in thread
From: Junio C Hamano @ 2009-01-02 10:55 UTC (permalink / raw)
To: Alexander Potashev; +Cc: Git Mailing List
A git patch that does not change the executable bit still records the mode
on its "index" line. "git apply" used to interpret this mode exactly the
same way as it interprets the mode recorded on "new mode" line. As the
wish by the patch submitter to set the mode to the one recorded on the
line.
The reason the mode does not agree between the submitter and the receiver
in the first place is because there is _another_ commit that only appears
on one side but not the other since their histories diverged, and that
commit changes the mode. The patch has "index" line but not "new mode"
line because its change is about updating the contents without affecting
the mode. The application of such a patch is an explicit wish by the
submitter to only cherry-pick the commit that updates the contents without
cherry-picking the commit that modifies the mode. Viewed this way, the
current behaviour is problematic, even though the command does warn when
the mode of the path being patched does not match this mode, and a careful
user could detect this inconsistencies between the patch submitter and the
patch receiver.
This changes the semantics of the mode recorded on the "index" line;
instead of interpreting it as the submitter's wish to set the mode to the
recorded value, it merely informs what the mode submitter happened to
have, and the presense of the "index" line is taken as submitter's wish to
keep whatever the mode is on the receiving end.
This is based on the patch originally done by Alexander Potashev with a
minor fix; the tests are mine.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Alexander Potashev <aspotashev@gmail.com> writes:
> Prevent 'git apply' from changing permissions without
> 'old mode'/'new mode' lines in patch.
> (WARNING: this changes the behaviour of 'git apply')
> ...
> Test cases are probably wanted, but I don't really know how to do them
> and I'll only give a chain of commands to reproduce the issue:
So here is what I sent earlier but with test cases. I suspect your
version does not pass the latter half of the test suite, because it stomps
on the explicitly recorded mode changes in the patch.
builtin-apply.c | 4 ++-
t/t4129-apply-samemode.sh | 62 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+), 1 deletions(-)
diff --git c/builtin-apply.c w/builtin-apply.c
index 07244b0..a8f75ed 100644
--- c/builtin-apply.c
+++ w/builtin-apply.c
@@ -630,7 +630,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
memcpy(patch->new_sha1_prefix, line, len);
patch->new_sha1_prefix[len] = 0;
if (*ptr == ' ')
- patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+ patch->old_mode = strtoul(ptr+1, NULL, 8);
return 0;
}
@@ -2447,6 +2447,8 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st_mode, patch->old_mode);
+ if (!patch->new_mode)
+ patch->new_mode = st_mode;
return 0;
is_new:
diff --git c/t/t4129-apply-samemode.sh w/t/t4129-apply-samemode.sh
new file mode 100755
index 0000000..adfcbb5
--- /dev/null
+++ w/t/t4129-apply-samemode.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='applying patch with mode bits'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo original >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git tag initial &&
+ echo modified >file &&
+ git diff --stat -p >patch-0.txt &&
+ chmod +x file &&
+ git diff --stat -p >patch-1.txt
+'
+
+test_expect_success 'same mode (no index)' '
+ git reset --hard &&
+ chmod +x file &&
+ git apply patch-0.txt &&
+ test -x file
+'
+
+test_expect_success 'same mode (with index)' '
+ git reset --hard &&
+ chmod +x file &&
+ git add file &&
+ git apply --index patch-0.txt &&
+ test -x file &&
+ git diff --exit-code
+'
+
+test_expect_success 'same mode (index only)' '
+ git reset --hard &&
+ chmod +x file &&
+ git add file &&
+ git apply --cached patch-0.txt &&
+ git ls-files -s file | grep "^100755"
+'
+
+test_expect_success 'mode update (no index)' '
+ git reset --hard &&
+ git apply patch-1.txt &&
+ test -x file
+'
+
+test_expect_success 'mode update (with index)' '
+ git reset --hard &&
+ git apply --index patch-1.txt &&
+ test -x file &&
+ git diff --exit-code
+'
+
+test_expect_success 'mode update (index only)' '
+ git reset --hard &&
+ git apply --cached patch-1.txt &&
+ git ls-files -s file | grep "^100755"
+'
+
+test_done
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC PATCH] builtin-apply: prevent non-explicit permission changes
2009-01-02 0:56 ` Junio C Hamano
@ 2009-01-02 13:37 ` Alexander Potashev
0 siblings, 0 replies; 7+ messages in thread
From: Alexander Potashev @ 2009-01-02 13:37 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Git Mailing List
On 16:56 Thu 01 Jan , Junio C Hamano wrote:
> Alexander Potashev <aspotashev@gmail.com> writes:
>
> > On 05:00 Thu 01 Jan , Junio C Hamano wrote:
> >> Alexander Potashev <aspotashev@gmail.com> writes:
> > ...
> >> > @@ -2447,6 +2447,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
> >> > if (st_mode != patch->old_mode)
> >> > fprintf(stderr, "warning: %s has type %o, expected %o\n",
> >> > old_name, st_mode, patch->old_mode);
> >> > + patch->new_mode = st_mode;
> >>
> >> Can you do this unconditionally, overwriting whatever we read from the
> >> patch header metainfo lines?
> >
> > Do you mean overwriting of 'patch->new_mode' right after patch parsing?
>
> My question was if we should assign st_mode to new_mode _unconditionally_
> here, even when patch->new_mode has already been read from the explicit
> mode change line (i.e. "new mode ", line not "index "line) of the patch
> input.
>
> The call-chain of the program looks like this:
>
> -> apply_patch()
> -> parse_chunk()
> -> find_header()
> * initialize new_mode and old_mode to 0
> -> parse_git_header()
> * set new_mode and old_mode from the patch metainfo, i.e.
> "new mode", "old mode" and "index" lines.
> -> parse_single_patch()
> -> check_patch_list()
> -> check_patch()
> -> check_preimage()
> * make sure there is no local mods
> * warn if old_mode read from the patch (i.e. the preimage file
> the patch submitter used to prepare the patch against) does not
> match what we have
> * warn about mode inconsistency (e.g. the patch submitter thinks
> the mode should be 0644 but our tree has 0755).
> -> apply_data()
> -> write_out_results()
> -> write_out_one_result(0)
> * delete old
> -> write_out_one_result(1)
> * create new
>
> Currently the mode 100644 on the "index" line in a patch is handled
> exactly in the same way as having "old mode 100644" and "new mode 100644"
> lines in the metainfo. The patch submitter claims to have started from
> 100644 and he claims that he wants to have 100644 as the result. That is
> why there is a warning in check_patch().
>
> If we stop reading the new mode from the "index" line (but we still read
> "old_mode" there) without any other change you made in your patch, what
> breaks (i.e. without the patch->new_mode assignment hunk)? I haven't
> followed the codepath too closely, and I suspect you found some cases
> where new_mode stays 0 as initialized, and that may be the reason you have
> this assignment.
>
> But the assignment being unconditional bothered me a lot.
>
> I tend to agree that the current "The final mode bits I want to have on
> this path is this" semantics we give to the "index" line is much less
> useful and less sane and it is a good idea to redefine it as "FYI, the
> copy I made this patch against had this mode bits. I do not intend to
> change the mode bits of the path with this patch."
>
> builtin-apply.c | 4 +++-
> 1 files changed, 3 insertions(+), 1 deletions(-)
>
> diff --git c/builtin-apply.c w/builtin-apply.c
> index 07244b0..a8f75ed 100644
> --- c/builtin-apply.c
> +++ w/builtin-apply.c
> @@ -630,7 +630,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
> memcpy(patch->new_sha1_prefix, line, len);
> patch->new_sha1_prefix[len] = 0;
> if (*ptr == ' ')
> - patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
> + patch->old_mode = strtoul(ptr+1, NULL, 8);
> return 0;
> }
>
> @@ -2447,6 +2447,8 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
> if (st_mode != patch->old_mode)
> fprintf(stderr, "warning: %s has type %o, expected %o\n",
> old_name, st_mode, patch->old_mode);
> + if (!patch->new_mode)
> + patch->new_mode = st_mode;
This is a _major_ fix, with my patch it would never change any
permissions at all.
I couldn't fully understand that problem last night, sorry for the
noise.
> return 0;
>
> is_new:
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH] builtin-apply: prevent non-explicit permission changes
2009-01-02 10:55 ` Junio C Hamano
@ 2009-01-02 17:35 ` Jeff King
0 siblings, 0 replies; 7+ messages in thread
From: Jeff King @ 2009-01-02 17:35 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Alexander Potashev, Git Mailing List
On Fri, Jan 02, 2009 at 02:55:37AM -0800, Junio C Hamano wrote:
> A git patch that does not change the executable bit still records the mode
> on its "index" line. "git apply" used to interpret this mode exactly the
> same way as it interprets the mode recorded on "new mode" line. As the
> wish by the patch submitter to set the mode to the one recorded on the
> line.
Nit: I had to read that third sentence several times to make sense of
it, since it is not a complete sentence (I think s/line\. As/line: as/
might help).
> This changes the semantics of the mode recorded on the "index" line;
> instead of interpreting it as the submitter's wish to set the mode to the
> recorded value, it merely informs what the mode submitter happened to
> have, and the presense of the "index" line is taken as submitter's wish to
> keep whatever the mode is on the receiving end.
I have been following this thread but didn't have a chance to look
closely until now. I think this change is definitely the right thing, as
it follows the normal semantics for a patch (which are basically a
merge: "change the parts we changed, but leave everything else, even if
the other side changed it").
> builtin-apply.c | 4 ++-
The implementation looks good to me.
> t/t4129-apply-samemode.sh | 62 ++++++++++++++++++++++++++++++++++++++++
And the tests make me feel warm and fuzzy. It is always nice to see
tests that aren't just "X was broken, and now it works" or "new feature
Y works" but "here is every case spelled out with its desired behavior."
I think those are the tests that really help keep us regression-proof.
-Peff
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2009-01-02 17:36 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-12-30 23:53 [RFC PATCH] builtin-apply: prevent non-explicit permission changes Alexander Potashev
2009-01-01 13:00 ` Junio C Hamano
2009-01-01 22:17 ` Alexander Potashev
2009-01-02 0:56 ` Junio C Hamano
2009-01-02 13:37 ` Alexander Potashev
2009-01-02 10:55 ` Junio C Hamano
2009-01-02 17:35 ` Jeff King
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).