* Honoring a checked out gitattributes file
@ 2009-01-28 15:25 Kristian Amlie
2009-01-28 16:44 ` Michael J Gruber
2009-01-28 17:55 ` Honoring a checked out gitattributes file Jeff King
0 siblings, 2 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-01-28 15:25 UTC (permalink / raw)
To: git
Hi!
We currently use msysGit in our company test farm to checkout the latest
development branch and do autotests. However, we have one problem:
Certain files require UNIX line endings, even though this is a Windows
system. For this we use .gitattributes.
However, if the .gitattributes file is also checked in to the branch, it
will not always be honored. I browsed the code a bit, and it seems to
happen whenever there is an existing .gitattributes file, but the
checkout adds new files to it. These new files will not get the correct
line endings (although I'm not sure if it happens *every* time, it could
depend on the order they are checked out).
I think this should be fairly straightforward to fix, by ensuring that
if there is a file called .gitattributes in the index of the current
directory, check it out first, before all the other files that may be
affected by it. I can produce a patch and testcase for it, but I wanted
to hear the opinions of some developers in case there is an obvious flaw
in my solution.
What do you think?
--
Kristian Amlie
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: Honoring a checked out gitattributes file
2009-01-28 15:25 Honoring a checked out gitattributes file Kristian Amlie
@ 2009-01-28 16:44 ` Michael J Gruber
2009-01-28 17:25 ` Kristian Amlie
2009-01-28 17:55 ` Honoring a checked out gitattributes file Jeff King
1 sibling, 1 reply; 22+ messages in thread
From: Michael J Gruber @ 2009-01-28 16:44 UTC (permalink / raw)
To: Kristian Amlie; +Cc: git
Kristian Amlie venit, vidit, dixit 28.01.2009 16:25:
> Hi!
>
> We currently use msysGit in our company test farm to checkout the latest
> development branch and do autotests. However, we have one problem:
> Certain files require UNIX line endings, even though this is a Windows
> system. For this we use .gitattributes.
>
> However, if the .gitattributes file is also checked in to the branch, it
> will not always be honored. I browsed the code a bit, and it seems to
> happen whenever there is an existing .gitattributes file, but the
> checkout adds new files to it. These new files will not get the correct
> line endings (although I'm not sure if it happens *every* time, it could
> depend on the order they are checked out).
>
> I think this should be fairly straightforward to fix, by ensuring that
> if there is a file called .gitattributes in the index of the current
> directory, check it out first, before all the other files that may be
> affected by it. I can produce a patch and testcase for it, but I wanted
> to hear the opinions of some developers in case there is an obvious flaw
> in my solution.
>
> What do you think?
I think there's a general time ordering problem. Say you do the
following commits:
A: add a.txt
B: add a .gitattributes file covering *.txt (say with crlf or any filter)
C: add c.txt
Now, with an empty dir, you do either
1) checkout A, B, C sequentially
2) checkout C
The contents of the checkout will be different in cases 1) and 2):
1) a.txt is checked out out as is, c.txt according to the attributes
2) with current git: probably like 1), with your suggestion: both a.txt
and c.txt filtered according to the attributes.
If you add a file and .gitattributes covering it in the same commit
there is an ordering ambiguity which can be solved (patched away)
easily, but I think the difference between 1) and 2) is still
problematic, and would need to be dealt with.
The main problem seems to be that changing a file like gitattributes can
potentially change (by changing filters) the contents which should be
stored for a different file even if the checkout of that file doesn't
change.
Cheers,
Michael
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: Honoring a checked out gitattributes file
2009-01-28 16:44 ` Michael J Gruber
@ 2009-01-28 17:25 ` Kristian Amlie
2009-01-30 13:00 ` Kristian Amlie
0 siblings, 1 reply; 22+ messages in thread
From: Kristian Amlie @ 2009-01-28 17:25 UTC (permalink / raw)
To: Michael J Gruber; +Cc: git
Michael J Gruber wrote:
> I think there's a general time ordering problem. Say you do the
> following commits:
>
> A: add a.txt
> B: add a .gitattributes file covering *.txt (say with crlf or any filter)
> C: add c.txt
>
> Now, with an empty dir, you do either
>
> 1) checkout A, B, C sequentially
> 2) checkout C
>
> The contents of the checkout will be different in cases 1) and 2):
> 1) a.txt is checked out out as is, c.txt according to the attributes
> 2) with current git: probably like 1), with your suggestion: both a.txt
> and c.txt filtered according to the attributes.
>
> If you add a file and .gitattributes covering it in the same commit
> there is an ordering ambiguity which can be solved (patched away)
> easily, but I think the difference between 1) and 2) is still
> problematic, and would need to be dealt with.
I agree.
>
> The main problem seems to be that changing a file like gitattributes can
> potentially change (by changing filters) the contents which should be
> stored for a different file even if the checkout of that file doesn't
> change.
Yes, that is a problem. Ideally, the crlf attribute would be tied to the
file entry itself rather than a separate file (so changing the attribute
would mean a change to the file), but I guess we are stuck with what we
have.
I still think that case 2) is the most common, and fixing it has the
appealing property that if the repository line endings are broken for
some reason (because of case 1 or something else), then recloning or
checking out from scratch is guaranteed to bring the working tree into
the "correct" state.
Since fixing both cases is a pretty big task and fixing only case 2 is
small, I propose that we go ahead with that.
--
Kristian Amlie
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: Honoring a checked out gitattributes file
2009-01-28 15:25 Honoring a checked out gitattributes file Kristian Amlie
2009-01-28 16:44 ` Michael J Gruber
@ 2009-01-28 17:55 ` Jeff King
1 sibling, 0 replies; 22+ messages in thread
From: Jeff King @ 2009-01-28 17:55 UTC (permalink / raw)
To: Kristian Amlie; +Cc: git
On Wed, Jan 28, 2009 at 04:25:37PM +0100, Kristian Amlie wrote:
> However, if the .gitattributes file is also checked in to the branch, it
> will not always be honored. I browsed the code a bit, and it seems to
> happen whenever there is an existing .gitattributes file, but the
> checkout adds new files to it. These new files will not get the correct
> line endings (although I'm not sure if it happens *every* time, it could
> depend on the order they are checked out).
This is a known limitation of gitattributes. There has been some
discussion in the past on how it should work, but I don't recall the
specifics; try searching the list archive. I think it is really just
waiting for somebody to step up and write some patches.
As a workaround, you might be able to do something like:
branch=master
git show $branch:.gitattributes >.git/info/attributes
git checkout $branch
which is very hacky, but might work depending on your setup. Notably it
will overwrite any actual use you were making of .git/info/attributes,
and it will not respect any .gitattributes files in subdirectories.
-Peff
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: Honoring a checked out gitattributes file
2009-01-28 17:25 ` Kristian Amlie
@ 2009-01-30 13:00 ` Kristian Amlie
2009-01-30 13:00 ` [PATCH] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
2009-03-12 9:36 ` Honoring a checked out gitattributes file Kristian Amlie
0 siblings, 2 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-01-30 13:00 UTC (permalink / raw)
To: git
I have now created a test case which demonstrates the problem.
I want to create a patch for it too, but I am unfortunately not so
familiar with the Git source code.
To put it short, I want to make sure that when checking out a tree,
.gitattributes will be checked out before all other files, so that
files that are affected by it will be guaranteed to get the correct
attributes. Maybe someone from this list could point me in the right
direction in the source code for something like this?
--
Kristian Amlie
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH] Add a test for checking whether gitattributes is honored by checkout.
2009-01-30 13:00 ` Kristian Amlie
@ 2009-01-30 13:00 ` Kristian Amlie
2009-03-12 9:36 ` Honoring a checked out gitattributes file Kristian Amlie
1 sibling, 0 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-01-30 13:00 UTC (permalink / raw)
To: git; +Cc: Kristian Amlie
The original bug will not honor new entries in gitattributes if they
are changed in the same checkout as the files they affect.
---
t/t2012-checkout-crlf.sh | 29 +++++++++++++++++++++++++++++
1 files changed, 29 insertions(+), 0 deletions(-)
create mode 100755 t/t2012-checkout-crlf.sh
diff --git a/t/t2012-checkout-crlf.sh b/t/t2012-checkout-crlf.sh
new file mode 100755
index 0000000..cb997a8
--- /dev/null
+++ b/t/t2012-checkout-crlf.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description='checkout should honor .gitattributes'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ git config core.autocrlf true &&
+ printf "dummy dummy=true\r\n" > .gitattributes &&
+ git add .gitattributes &&
+ git commit -m initial &&
+ printf "file -crlf\n" >> .gitattributes &&
+ printf "contents\n" > file &&
+ git add .gitattributes file &&
+ git commit -m second
+
+'
+
+test_expect_success 'checkout with existing .gitattributes' '
+
+ git checkout master~1 &&
+ git checkout master &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_done
+
--
1.6.0.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: Honoring a checked out gitattributes file
2009-01-30 13:00 ` Kristian Amlie
2009-01-30 13:00 ` [PATCH] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
@ 2009-03-12 9:36 ` Kristian Amlie
2009-03-12 9:36 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
1 sibling, 1 reply; 22+ messages in thread
From: Kristian Amlie @ 2009-03-12 9:36 UTC (permalink / raw)
To: git
Ok, it took a bit longer than I thought to get around to this, but I
finally have a patch for the problem. I am not so familiar with the
Git source code, so I apologize if I do something incredibly stupid.
Hopefully the commit message should explain what I do, but please ask
questions if it's not clear. I'm sure there's room for improvements.
--
Kristian
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout.
2009-03-12 9:36 ` Honoring a checked out gitattributes file Kristian Amlie
@ 2009-03-12 9:36 ` Kristian Amlie
2009-03-12 9:36 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
2009-03-12 9:47 ` Matthieu Moy
0 siblings, 2 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-03-12 9:36 UTC (permalink / raw)
To: git; +Cc: Kristian Amlie
The original bug will not honor new entries in gitattributes if they
are changed in the same checkout as the files they affect.
---
t/t2013-checkout-crlf.sh | 29 +++++++++++++++++++++++++++++
1 files changed, 29 insertions(+), 0 deletions(-)
create mode 100755 t/t2013-checkout-crlf.sh
diff --git a/t/t2013-checkout-crlf.sh b/t/t2013-checkout-crlf.sh
new file mode 100755
index 0000000..cb997a8
--- /dev/null
+++ b/t/t2013-checkout-crlf.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description='checkout should honor .gitattributes'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ git config core.autocrlf true &&
+ printf "dummy dummy=true\r\n" > .gitattributes &&
+ git add .gitattributes &&
+ git commit -m initial &&
+ printf "file -crlf\n" >> .gitattributes &&
+ printf "contents\n" > file &&
+ git add .gitattributes file &&
+ git commit -m second
+
+'
+
+test_expect_success 'checkout with existing .gitattributes' '
+
+ git checkout master~1 &&
+ git checkout master &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_done
+
--
1.6.2.105.g16bc7.dirty
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-12 9:36 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
@ 2009-03-12 9:36 ` Kristian Amlie
2009-03-12 9:59 ` Johannes Sixt
2009-03-12 9:47 ` Matthieu Moy
1 sibling, 1 reply; 22+ messages in thread
From: Kristian Amlie @ 2009-03-12 9:36 UTC (permalink / raw)
To: git; +Cc: Kristian Amlie
We do this by popping off elements on the attribute stack, until we
reach the level where a new .gitattributes was checked out. The next
time someone calls git_checkattr(), it will reconstruct the
attributes from that point.
---
attr.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------
attr.h | 1 +
entry.c | 23 ++++++++++++++++++++
3 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/attr.c b/attr.c
index 17f6a4d..7deb51a 100644
--- a/attr.c
+++ b/attr.c
@@ -455,6 +455,23 @@ static void bootstrap_attr_stack(void)
}
}
+static void pop_attr_stack(const char *path, int dirlen)
+{
+ struct attr_stack *elem;
+ while (attr_stack && attr_stack->origin) {
+ int namelen = strlen(attr_stack->origin);
+
+ elem = attr_stack;
+ if (namelen <= dirlen &&
+ !strncmp(elem->origin, path, namelen))
+ break;
+
+ debug_pop(elem);
+ attr_stack = elem->prev;
+ free_attr_elem(elem);
+ }
+}
+
static void prepare_attr_stack(const char *path, int dirlen)
{
struct attr_stack *elem, *info;
@@ -489,18 +506,7 @@ static void prepare_attr_stack(const char *path, int dirlen)
* Pop the ones from directories that are not the prefix of
* the path we are checking.
*/
- while (attr_stack && attr_stack->origin) {
- int namelen = strlen(attr_stack->origin);
-
- elem = attr_stack;
- if (namelen <= dirlen &&
- !strncmp(elem->origin, path, namelen))
- break;
-
- debug_pop(elem);
- attr_stack = elem->prev;
- free_attr_elem(elem);
- }
+ pop_attr_stack(path, dirlen);
/*
* Read from parent directories and push them down
@@ -642,3 +648,45 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
return 0;
}
+
+void git_attr_invalidate_path(const char *path)
+{
+ int dirlen;
+ const char *cp;
+ struct attr_stack *info, *elem;
+
+ bootstrap_attr_stack();
+
+ /*
+ * Pop the "info" one that is always at the top of the stack.
+ */
+ info = attr_stack;
+ attr_stack = info->prev;
+
+ cp = strrchr(path, '/');
+ dirlen = cp ? cp - path : 0;
+ /* Pop everything up to, and including, path. */
+ pop_attr_stack(path, dirlen);
+
+ if (!strcmp(path, "") && attr_stack->origin && !strcmp(attr_stack->origin, "")) {
+ /* Special handling when the root attributes must be invalidated. */
+ elem = attr_stack;
+ debug_pop(elem);
+ attr_stack = elem->prev;
+ free_attr_elem(elem);
+
+ if (!is_bare_repository()) {
+ elem = read_attr(GITATTRIBUTES_FILE, 1);
+ elem->origin = strdup("");
+ elem->prev = attr_stack;
+ attr_stack = elem;
+ debug_push(elem);
+ }
+ }
+
+ /*
+ * Finally push the "info" one at the top of the stack.
+ */
+ info->prev = attr_stack;
+ attr_stack = info;
+}
diff --git a/attr.h b/attr.h
index f1c2038..8f4135b 100644
--- a/attr.h
+++ b/attr.h
@@ -30,5 +30,6 @@ struct git_attr_check {
};
int git_checkattr(const char *path, int, struct git_attr_check *);
+void git_attr_invalidate_path(const char *path);
#endif /* ATTR_H */
diff --git a/entry.c b/entry.c
index 05aa58d..121c979 100644
--- a/entry.c
+++ b/entry.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "blob.h"
#include "dir.h"
+#include "attr.h"
static void create_directories(const char *path, const struct checkout *state)
{
@@ -91,6 +92,9 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
{
int fd;
long wrote;
+ int gitattrlen;
+ int pathlen;
+ char *inv_path;
switch (ce->ce_mode & S_IFMT) {
char *new;
@@ -171,6 +175,25 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
return error("git checkout-index: unknown file mode for %s", path);
}
+ gitattrlen = strlen(GITATTRIBUTES_FILE);
+ pathlen = strlen(path);
+ if (!strncmp(path + pathlen - gitattrlen, GITATTRIBUTES_FILE, gitattrlen)) {
+ /* Invalidate attributes if a new .gitattributes file was checked out. */
+ inv_path = strrchr(path, '/');
+ if (!inv_path) {
+ pathlen = 0;
+ inv_path = xmalloc(1);
+ *inv_path = '\0';
+ } else {
+ pathlen = inv_path - path;
+ inv_path = xmalloc(pathlen + 1);
+ strncpy(inv_path, path, pathlen);
+ inv_path[pathlen] = '\0';
+ }
+ git_attr_invalidate_path(inv_path);
+ free(inv_path);
+ }
+
if (state->refresh_cache) {
struct stat st;
lstat(ce->name, &st);
--
1.6.2.105.g16bc7.dirty
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout.
2009-03-12 9:36 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
2009-03-12 9:36 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
@ 2009-03-12 9:47 ` Matthieu Moy
2009-03-12 9:53 ` Kristian Amlie
1 sibling, 1 reply; 22+ messages in thread
From: Matthieu Moy @ 2009-03-12 9:47 UTC (permalink / raw)
To: Kristian Amlie; +Cc: git
Kristian Amlie <kristian.amlie@nokia.com> writes:
> +test_expect_success 'setup' '
If you have two separate patches for test and bugfix, you probably
want the first to introduce test_expect_failure, and the second to
change it to test_expect_success. This way: 1) the test-suite passes
for all commits (and git-bisect will be your friend again), and 2) the
second patch is self-explanatory about the bug it fixes.
--
Matthieu
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout.
2009-03-12 9:47 ` Matthieu Moy
@ 2009-03-12 9:53 ` Kristian Amlie
0 siblings, 0 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-03-12 9:53 UTC (permalink / raw)
To: ext Matthieu Moy; +Cc: git@vger.kernel.org
ext Matthieu Moy wrote:
> Kristian Amlie <kristian.amlie@nokia.com> writes:
>
>> +test_expect_success 'setup' '
>
> If you have two separate patches for test and bugfix, you probably
> want the first to introduce test_expect_failure, and the second to
> change it to test_expect_success. This way: 1) the test-suite passes
> for all commits (and git-bisect will be your friend again), and 2) the
> second patch is self-explanatory about the bug it fixes.
Good point. I'll fix that in my next batch.
--
Kristian
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-12 9:36 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
@ 2009-03-12 9:59 ` Johannes Sixt
2009-03-12 10:23 ` Kristian Amlie
2009-03-13 13:24 ` Honoring a checked out gitattributes file Kristian Amlie
0 siblings, 2 replies; 22+ messages in thread
From: Johannes Sixt @ 2009-03-12 9:59 UTC (permalink / raw)
To: Kristian Amlie; +Cc: git
Kristian Amlie schrieb:
> We do this by popping off elements on the attribute stack, until we
> reach the level where a new .gitattributes was checked out. The next
> time someone calls git_checkattr(), it will reconstruct the
> attributes from that point.
...
> + gitattrlen = strlen(GITATTRIBUTES_FILE);
> + pathlen = strlen(path);
> + if (!strncmp(path + pathlen - gitattrlen, GITATTRIBUTES_FILE, gitattrlen)) {
> + /* Invalidate attributes if a new .gitattributes file was checked out. */
But if there was file .abc.txt that was checked out before .gitattributes
was checked out, then the new .gitattributes won't have been used for
.abc.txt, yet, right?
(Disclaimer: I didn't read the code, just your description and this comment.)
-- Hannes
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-12 9:59 ` Johannes Sixt
@ 2009-03-12 10:23 ` Kristian Amlie
2009-03-13 13:24 ` Honoring a checked out gitattributes file Kristian Amlie
1 sibling, 0 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-03-12 10:23 UTC (permalink / raw)
To: ext Johannes Sixt; +Cc: git@vger.kernel.org
ext Johannes Sixt wrote:
> Kristian Amlie schrieb:
>> We do this by popping off elements on the attribute stack, until we
>> reach the level where a new .gitattributes was checked out. The next
>> time someone calls git_checkattr(), it will reconstruct the
>> attributes from that point.
> ...
>> + gitattrlen = strlen(GITATTRIBUTES_FILE);
>> + pathlen = strlen(path);
>> + if (!strncmp(path + pathlen - gitattrlen, GITATTRIBUTES_FILE, gitattrlen)) {
>> + /* Invalidate attributes if a new .gitattributes file was checked out. */
>
> But if there was file .abc.txt that was checked out before .gitattributes
> was checked out, then the new .gitattributes won't have been used for
> .abc.txt, yet, right?
You're right, that fails.
I'm guessing my initial patch (and testcase) works because the sorting
is alphabetical and .gitattributes happens to be checked out before
files with letters. I would need to make sure that .gitattributes gets
checked out first.
--
Kristian
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: Honoring a checked out gitattributes file
2009-03-12 9:59 ` Johannes Sixt
2009-03-12 10:23 ` Kristian Amlie
@ 2009-03-13 13:24 ` Kristian Amlie
2009-03-13 13:24 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
1 sibling, 1 reply; 22+ messages in thread
From: Kristian Amlie @ 2009-03-13 13:24 UTC (permalink / raw)
To: git
Ok, here's another round. I fixed the test case to expect failure, as
pointed out by Matthieu, and I also added code to make sure that
.gitattributes gets checked out first.
I also added a test case and some code to support the case where
.gitattributes is removed in a commit, but this doesn't work properly
yet (see commit message). I'm not sure how to solve this without
resolving to ugly hacks like passing the new index_state* all the way
down to where git_checkattr gets called. If anybody has any
suggestions, do share.
The main usecase where .gitattributes is modified, is anyway covered
by this patch.
--
Kristian
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout.
2009-03-13 13:24 ` Honoring a checked out gitattributes file Kristian Amlie
@ 2009-03-13 13:24 ` Kristian Amlie
2009-03-13 13:24 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
2009-03-14 4:36 ` [PATCH 1/2] " Junio C Hamano
0 siblings, 2 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-03-13 13:24 UTC (permalink / raw)
To: git; +Cc: Kristian Amlie
The original bug will not honor new entries in gitattributes if they
are changed in the same checkout as the files they affect.
It will also keep using .gitattributes, even if it is deleted in the
same commit as the files it affects.
---
t/t2013-checkout-crlf.sh | 41 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 41 insertions(+), 0 deletions(-)
create mode 100755 t/t2013-checkout-crlf.sh
diff --git a/t/t2013-checkout-crlf.sh b/t/t2013-checkout-crlf.sh
new file mode 100755
index 0000000..d9d6465
--- /dev/null
+++ b/t/t2013-checkout-crlf.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='checkout should honor .gitattributes'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ git config core.autocrlf true &&
+ printf ".file2 -crlf\r\n" > .gitattributes &&
+ git add .gitattributes &&
+ git commit -m initial &&
+ printf ".file -crlf\n" >> .gitattributes &&
+ printf "contents\n" > .file &&
+ git add .gitattributes .file &&
+ git commit -m second &&
+ git rm .gitattributes &&
+ printf "contents\r\n" > .file2 &&
+ git add .file2 &&
+ git commit -m third
+
+'
+
+test_expect_failure 'checkout with existing .gitattributes' '
+
+ git checkout master~2 &&
+ git checkout master~1 &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_failure 'checkout when deleting .gitattributes' '
+
+ git checkout master~1 &&
+ git checkout master &&
+ test "$(stat -c %s .file2)" = "10"
+
+'
+
+test_done
+
--
1.6.1.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-13 13:24 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
@ 2009-03-13 13:24 ` Kristian Amlie
2009-03-14 4:17 ` Junio C Hamano
2009-03-14 4:36 ` [PATCH 1/2] " Junio C Hamano
1 sibling, 1 reply; 22+ messages in thread
From: Kristian Amlie @ 2009-03-13 13:24 UTC (permalink / raw)
To: git; +Cc: Kristian Amlie
The fix is twofold:
First, we force .gitattributes files to always be the first ones
checked out. This is the part in check_updates().
Second, we make sure that the checked out attributes get honored by
popping off elements on the attribute stack, until we reach the level
where a new .gitattributes was checked out. The next time someone
calls git_checkattr(), it will reconstruct the attributes from that
point.
Note that in unlink_entry() there is code to support the case where
.gitattributes is removed (test case #3 in t2013-checkout-crlf.sh),
but this doesn't work properly because Git tries to read new
attributes from the index, but the old index (where .gitattributes
still exists) is still active.
---
attr.c | 86 +++++++++++++++++++++++++++++++++++++++------
attr.h | 1 +
entry.c | 19 ++++++++++
t/t2013-checkout-crlf.sh | 2 +-
unpack-trees.c | 77 +++++++++++++++++++++++++++++++----------
5 files changed, 154 insertions(+), 31 deletions(-)
diff --git a/attr.c b/attr.c
index 17f6a4d..2bc0847 100644
--- a/attr.c
+++ b/attr.c
@@ -455,6 +455,23 @@ static void bootstrap_attr_stack(void)
}
}
+static void pop_attr_stack(const char *path, int dirlen)
+{
+ struct attr_stack *elem;
+ while (attr_stack && attr_stack->origin) {
+ int namelen = strlen(attr_stack->origin);
+
+ elem = attr_stack;
+ if (namelen <= dirlen &&
+ !strncmp(elem->origin, path, namelen))
+ break;
+
+ debug_pop(elem);
+ attr_stack = elem->prev;
+ free_attr_elem(elem);
+ }
+}
+
static void prepare_attr_stack(const char *path, int dirlen)
{
struct attr_stack *elem, *info;
@@ -489,18 +506,7 @@ static void prepare_attr_stack(const char *path, int dirlen)
* Pop the ones from directories that are not the prefix of
* the path we are checking.
*/
- while (attr_stack && attr_stack->origin) {
- int namelen = strlen(attr_stack->origin);
-
- elem = attr_stack;
- if (namelen <= dirlen &&
- !strncmp(elem->origin, path, namelen))
- break;
-
- debug_pop(elem);
- attr_stack = elem->prev;
- free_attr_elem(elem);
- }
+ pop_attr_stack(path, dirlen);
/*
* Read from parent directories and push them down
@@ -642,3 +648,59 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
return 0;
}
+
+void git_attr_invalidate_path(const char *path)
+{
+ int dirlen;
+ const char *cp;
+ struct attr_stack *info, *elem;
+
+ bootstrap_attr_stack();
+
+ /*
+ * Pop the "info" one that is always at the top of the stack.
+ */
+ info = attr_stack;
+ attr_stack = info->prev;
+
+ cp = strrchr(path, '/');
+ dirlen = cp ? cp - path : 0;
+ /* Pop everything up to, and including, path. */
+ pop_attr_stack(path, dirlen);
+
+ if (!strcmp(path, "") && attr_stack->origin && !strcmp(attr_stack->origin, "")) {
+ /*
+ * Special handling when the root attributes must be invalidated.
+ * Note that we cannot reread the attributes here. The reason is that
+ * when unlinking the root .gitattributes file, the index entry is
+ * removed after unlinking. If we reread the attributes here, we
+ * would get the same attributes as before because .gitattributes
+ * is still in the index. Thus we must wait until the next call to
+ * read_attr, when the index has been updated.
+ */
+ /* $GIT_DIR/info/attributes */
+ elem = info;
+ debug_pop(elem);
+ free_attr_elem(elem);
+
+ /* $GIT_WORK_TREE/.gitattributes */
+ elem = attr_stack;
+ debug_pop(elem);
+ attr_stack = elem->prev;
+ free_attr_elem(elem);
+
+ /* builtins */
+ elem = attr_stack;
+ debug_pop(elem);
+ attr_stack = elem->prev;
+ free_attr_elem(elem);
+
+ assert(!attr_stack);
+ } else {
+ /*
+ * Finally push the "info" one at the top of the stack.
+ */
+ info->prev = attr_stack;
+ attr_stack = info;
+ }
+}
diff --git a/attr.h b/attr.h
index f1c2038..8f4135b 100644
--- a/attr.h
+++ b/attr.h
@@ -30,5 +30,6 @@ struct git_attr_check {
};
int git_checkattr(const char *path, int, struct git_attr_check *);
+void git_attr_invalidate_path(const char *path);
#endif /* ATTR_H */
diff --git a/entry.c b/entry.c
index 05aa58d..7fb27fd 100644
--- a/entry.c
+++ b/entry.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "blob.h"
#include "dir.h"
+#include "attr.h"
static void create_directories(const char *path, const struct checkout *state)
{
@@ -91,6 +92,9 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
{
int fd;
long wrote;
+ int gitattrlen;
+ int pathlen;
+ char *cp;
switch (ce->ce_mode & S_IFMT) {
char *new;
@@ -171,6 +175,21 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
return error("git checkout-index: unknown file mode for %s", path);
}
+ gitattrlen = strlen(GITATTRIBUTES_FILE);
+ pathlen = strlen(path);
+ if (pathlen >= gitattrlen && !strncmp(path + pathlen - gitattrlen,
+ GITATTRIBUTES_FILE, gitattrlen)) {
+ /* Invalidate attributes if a new .gitattributes file was checked out. */
+ cp = strrchr(path, '/');
+ if (!cp) {
+ git_attr_invalidate_path("");
+ } else {
+ *cp = '\0';
+ git_attr_invalidate_path(path);
+ *cp = '/';
+ }
+ }
+
if (state->refresh_cache) {
struct stat st;
lstat(ce->name, &st);
diff --git a/t/t2013-checkout-crlf.sh b/t/t2013-checkout-crlf.sh
index d9d6465..e704c93 100755
--- a/t/t2013-checkout-crlf.sh
+++ b/t/t2013-checkout-crlf.sh
@@ -21,7 +21,7 @@ test_expect_success 'setup' '
'
-test_expect_failure 'checkout with existing .gitattributes' '
+test_expect_success 'checkout with existing .gitattributes' '
git checkout master~2 &&
git checkout master~1 &&
diff --git a/unpack-trees.c b/unpack-trees.c
index e547282..63a41f8 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -7,6 +7,7 @@
#include "unpack-trees.h"
#include "progress.h"
#include "refs.h"
+#include "attr.h"
/*
* Error messages expected by scripts out of plumbing commands such as
@@ -60,6 +61,7 @@ static void unlink_entry(struct cache_entry *ce)
{
char *cp, *prev;
char *name = ce->name;
+ int namelen, gitattrlen;
if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
return;
@@ -82,6 +84,21 @@ static void unlink_entry(struct cache_entry *ce)
}
prev = cp;
}
+
+ if (!prev) {
+ gitattrlen = strlen(GITATTRIBUTES_FILE);
+ namelen = strlen(name);
+ if (namelen >= gitattrlen && !strncmp(name + namelen - gitattrlen,
+ GITATTRIBUTES_FILE, gitattrlen)) {
+ if (cp) {
+ *cp = '\0';
+ git_attr_invalidate_path(name);
+ *cp = '/';
+ } else {
+ git_attr_invalidate_path("");
+ }
+ }
+ }
}
static struct checkout state;
@@ -92,6 +109,9 @@ static int check_updates(struct unpack_trees_options *o)
struct index_state *index = &o->result;
int i;
int errs = 0;
+ int attr;
+ int gitattrlen;
+ int namelen;
if (o->update && o->verbose_update) {
for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
@@ -105,27 +125,48 @@ static int check_updates(struct unpack_trees_options *o)
cnt = 0;
}
- for (i = 0; i < index->cache_nr; i++) {
- struct cache_entry *ce = index->cache[i];
+ gitattrlen = strlen(GITATTRIBUTES_FILE);
- if (ce->ce_flags & CE_REMOVE) {
- display_progress(progress, ++cnt);
- if (o->update)
- unlink_entry(ce);
- remove_index_entry_at(&o->result, i);
- i--;
- continue;
+ /*
+ * We want to checkout .gitattributes before everything else. So:
+ * attr == 0 -> .gitattributes
+ * attr == 1 -> everything else
+ */
+ for (attr = 0; attr < 2; attr++) {
+ for (i = 0; i < index->cache_nr; i++) {
+ struct cache_entry *ce = index->cache[i];
+
+ namelen = strlen(ce->name);
+ if ((attr == 0) != (namelen >= gitattrlen && strncmp(
+ ce->name + namelen - gitattrlen,
+ GITATTRIBUTES_FILE, gitattrlen) == 0))
+ continue;
+ if (ce->ce_flags & CE_REMOVE) {
+ display_progress(progress, ++cnt);
+ if (o->update)
+ unlink_entry(ce);
+ remove_index_entry_at(&o->result, i);
+ i--;
+ continue;
+ }
}
}
- for (i = 0; i < index->cache_nr; i++) {
- struct cache_entry *ce = index->cache[i];
+ for (attr = 0; attr < 2; attr++) {
+ for (i = 0; i < index->cache_nr; i++) {
+ struct cache_entry *ce = index->cache[i];
- if (ce->ce_flags & CE_UPDATE) {
- display_progress(progress, ++cnt);
- ce->ce_flags &= ~CE_UPDATE;
- if (o->update) {
- errs |= checkout_entry(ce, &state, NULL);
+ namelen = strlen(ce->name);
+ if ((attr == 0) != (namelen >= gitattrlen && strncmp(
+ ce->name + namelen - gitattrlen,
+ GITATTRIBUTES_FILE, gitattrlen) == 0))
+ continue;
+ if (ce->ce_flags & CE_UPDATE) {
+ display_progress(progress, ++cnt);
+ ce->ce_flags &= ~CE_UPDATE;
+ if (o->update) {
+ errs |= checkout_entry(ce, &state, NULL);
+ }
}
}
}
--
1.6.1.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-13 13:24 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
@ 2009-03-14 4:17 ` Junio C Hamano
2009-03-19 15:42 ` Kristian Amlie
0 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2009-03-14 4:17 UTC (permalink / raw)
To: Kristian Amlie; +Cc: git
Kristian Amlie <kristian.amlie@nokia.com> writes:
> The fix is twofold:
>
> First, we force .gitattributes files to always be the first ones
> checked out. This is the part in check_updates().
>
> Second, we make sure that the checked out attributes get honored by
> popping off elements on the attribute stack, until we reach the level
> where a new .gitattributes was checked out. The next time someone
> calls git_checkattr(), it will reconstruct the attributes from that
> point.
Yikes. The patch is too ugly beyond words.
In a well structured git program, we always read from the work tree and
the index (to see if there is something changed---you need to be able to
apply convert_to_git when you do this), internally compute what should
happen (e.g. decide that the new result needs to be checked out for a
path), and then write it out (you apply convert_to_working_tree while you
do this). So how about doing something like the attached patch?
The patch allows the caller to tell the usual "read from the working tree,
if not use the version from the index as the fallback" logic to be swapped
around, and take the index version. It may or may not pass your new tests
(I haven't even compile tested it), but I think the damage is minimized
compared to your version.
It is great that you are trying to fix this issue for the most obvious
"switching between two branches while not having a local change" case, but
frankly, I do not think this is solvable in more general cases, and that
is why it was kept "known to be broken, not worth fixing" state.
For example, you may notice that, after making a clean checkout, one path
has a wrong attribute assigned to it, and may try to correct it. But how?
$ edit .gitattributes ;# mark foo.dat as binary
$ rm foo.dat
$ git checkout foo.dat ;# make sure the new settings is correct???
Without this patch, this would have worked as expected, because we always
use the one from the work tree if available.
We have not "git add"ed .gitattributes yet because we would want to make
sure the change is correct before doing so. The patch takes attributes
for foo.dat from the old .gitattributes that is sitting in the index,
which still has the wrong information, so in that sense, it is a
regression.
To fix that, I _think_ you can further hack read_attr_from_index() to see
if the attribute file is marked with CE_UPDATE, i.e. it is something we
are checking out in this invocation of "check_updates()", and use it only
if it is, or something like that, but there probably are more corner cases
in which whichever one between the work tree and the index we take, it is
a wrong one.
There are other codepaths that call checkout_entry() that needs a
treatment similar to the one done to check_updates() by this patch, and I
suspect there are a few places that we do not even use checkout_entry().
I think you can fairly easily wrap write_out_results() in builtin-apply.c
in a similar way. I do not know what merge-recursive does offhand, but I
suspect it would be a lot harder to fix.
Anyway, here is the patch.
attr.c | 63 ++++++++++++++++++++++++++++++++++++++++++++-----------
attr.h | 6 +++++
unpack-trees.c | 3 ++
3 files changed, 59 insertions(+), 13 deletions(-)
diff --git a/attr.c b/attr.c
index 17f6a4d..72f6807 100644
--- a/attr.c
+++ b/attr.c
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
#include "attr.h"
@@ -318,6 +319,9 @@ static struct attr_stack *read_attr_from_array(const char **list)
return res;
}
+static enum git_attr_direction direction;
+static struct index_state *use_index;
+
static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
{
FILE *fp = fopen(path, "r");
@@ -340,9 +344,10 @@ static void *read_index_data(const char *path)
unsigned long sz;
enum object_type type;
void *data;
+ struct index_state *istate = use_index ? use_index : &the_index;
len = strlen(path);
- pos = cache_name_pos(path, len);
+ pos = index_name_pos(istate, path, len);
if (pos < 0) {
/*
* We might be in the middle of a merge, in which
@@ -350,15 +355,15 @@ static void *read_index_data(const char *path)
*/
int i;
for (i = -pos - 1;
- (pos < 0 && i < active_nr &&
- !strcmp(active_cache[i]->name, path));
+ (pos < 0 && i < istate->cache_nr &&
+ !strcmp(istate->cache[i]->name, path));
i++)
- if (ce_stage(active_cache[i]) == 2)
+ if (ce_stage(istate->cache[i]) == 2)
pos = i;
}
if (pos < 0)
return NULL;
- data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
+ data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
if (!data || type != OBJ_BLOB) {
free(data);
return NULL;
@@ -366,18 +371,12 @@ static void *read_index_data(const char *path)
return data;
}
-static struct attr_stack *read_attr(const char *path, int macro_ok)
+static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
{
struct attr_stack *res;
char *buf, *sp;
int lineno = 0;
- res = read_attr_from_file(path, macro_ok);
- if (res)
- return res;
-
- res = xcalloc(1, sizeof(*res));
-
/*
* There is no checked out .gitattributes file there, but
* we might have it in the index. We allow operation in a
@@ -385,8 +384,9 @@ static struct attr_stack *read_attr(const char *path, int macro_ok)
*/
buf = read_index_data(path);
if (!buf)
- return res;
+ return NULL;
+ res = xcalloc(1, sizeof(*res));
for (sp = buf; *sp; ) {
char *ep;
int more;
@@ -401,6 +401,25 @@ static struct attr_stack *read_attr(const char *path, int macro_ok)
return res;
}
+static struct attr_stack *read_attr(const char *path, int macro_ok)
+{
+ struct attr_stack *res;
+
+ if (direction == GIT_ATTR_CHECKOUT) {
+ res = read_attr_from_index(path, macro_ok);
+ if (!res)
+ res = read_attr_from_file(path, macro_ok);
+ }
+ else {
+ res = read_attr_from_file(path, macro_ok);
+ if (!res)
+ res = read_attr_from_index(path, macro_ok);
+ }
+ if (!res)
+ res = xcalloc(1, sizeof(*res));
+ return res;
+}
+
#if DEBUG_ATTR
static void debug_info(const char *what, struct attr_stack *elem)
{
@@ -428,6 +447,15 @@ static void debug_set(const char *what, const char *match, struct git_attr *attr
#define debug_set(a,b,c,d) do { ; } while (0)
#endif
+static void drop_attr_stack(void)
+{
+ while (attr_stack) {
+ struct attr_stack *elem = attr_stack;
+ attr_stack = elem->prev;
+ free_attr_elem(elem);
+ }
+}
+
static void bootstrap_attr_stack(void)
{
if (!attr_stack) {
@@ -642,3 +670,12 @@ int git_checkattr(const char *path, int num, struct git_attr_check *check)
return 0;
}
+
+void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
+{
+ enum git_attr_direction old = direction;
+ direction = new;
+ if (new != old)
+ drop_attr_stack();
+ use_index = istate;
+}
diff --git a/attr.h b/attr.h
index f1c2038..3a2f4ec 100644
--- a/attr.h
+++ b/attr.h
@@ -31,4 +31,10 @@ struct git_attr_check {
int git_checkattr(const char *path, int, struct git_attr_check *);
+enum git_attr_direction {
+ GIT_ATTR_CHECKIN,
+ GIT_ATTR_CHECKOUT
+};
+void git_attr_set_direction(enum git_attr_direction, struct index_state *);
+
#endif /* ATTR_H */
diff --git a/unpack-trees.c b/unpack-trees.c
index e547282..661218c 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -7,6 +7,7 @@
#include "unpack-trees.h"
#include "progress.h"
#include "refs.h"
+#include "attr.h"
/*
* Error messages expected by scripts out of plumbing commands such as
@@ -105,6 +106,7 @@ static int check_updates(struct unpack_trees_options *o)
cnt = 0;
}
+ git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
@@ -130,6 +132,7 @@ static int check_updates(struct unpack_trees_options *o)
}
}
stop_progress(&progress);
+ git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
return errs != 0;
}
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout.
2009-03-13 13:24 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
2009-03-13 13:24 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
@ 2009-03-14 4:36 ` Junio C Hamano
1 sibling, 0 replies; 22+ messages in thread
From: Junio C Hamano @ 2009-03-14 4:36 UTC (permalink / raw)
To: Kristian Amlie; +Cc: git
Please do not waste new tests for these. I think they can be added to
t0020-crlf.sh or t0021-conversion.sh.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-14 4:17 ` Junio C Hamano
@ 2009-03-19 15:42 ` Kristian Amlie
2009-03-19 21:06 ` Junio C Hamano
0 siblings, 1 reply; 22+ messages in thread
From: Kristian Amlie @ 2009-03-19 15:42 UTC (permalink / raw)
To: ext Junio C Hamano; +Cc: git@vger.kernel.org
ext Junio C Hamano wrote:
> In a well structured git program, we always read from the work tree and
> the index (to see if there is something changed---you need to be able to
> apply convert_to_git when you do this), internally compute what should
> happen (e.g. decide that the new result needs to be checked out for a
> path), and then write it out (you apply convert_to_working_tree while you
> do this). So how about doing something like the attached patch?
>
> The patch allows the caller to tell the usual "read from the working tree,
> if not use the version from the index as the fallback" logic to be swapped
> around, and take the index version. It may or may not pass your new tests
> (I haven't even compile tested it), but I think the damage is minimized
> compared to your version.
You're right, that's a much cleaner approach. The patch is really good
as far as I can see. It fixes both my test cases (including the one I
wasn't able to fix), and there are no additional failures when running
the autotests. Do you have second thoughts about it, or is it good
enough to apply?
If it is, I'll move the test case into t0021-conversion.sh like you said
and give you a new patch for that.
> It is great that you are trying to fix this issue for the most obvious
> "switching between two branches while not having a local change" case, but
> frankly, I do not think this is solvable in more general cases, and that
> is why it was kept "known to be broken, not worth fixing" state.
>
> For example, you may notice that, after making a clean checkout, one path
> has a wrong attribute assigned to it, and may try to correct it. But how?
>
> $ edit .gitattributes ;# mark foo.dat as binary
> $ rm foo.dat
> $ git checkout foo.dat ;# make sure the new settings is correct???
As far as I can see, this works without any modifications to the patch.
Is that maybe because git_attr_set_direction() is not called if you use
that form of checkout?
--
Kristian
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-19 15:42 ` Kristian Amlie
@ 2009-03-19 21:06 ` Junio C Hamano
2009-03-20 8:11 ` Kristian Amlie
0 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2009-03-19 21:06 UTC (permalink / raw)
To: Kristian Amlie; +Cc: ext Junio C Hamano, git@vger.kernel.org
Kristian Amlie <kristian.amlie@nokia.com> writes:
> ext Junio C Hamano wrote:
> ...
>> For example, you may notice that, after making a clean checkout, one path
>> has a wrong attribute assigned to it, and may try to correct it. But how?
>>
>> $ edit .gitattributes ;# mark foo.dat as binary
>> $ rm foo.dat
>> $ git checkout foo.dat ;# make sure the new settings is correct???
>
> As far as I can see, this works without any modifications to the patch.
> Is that maybe because git_attr_set_direction() is not called if you use
> that form of checkout?
But that in itself can be seen as a bug, right? In another use case,
suppose you botched your .gitattributes in HEAD version and noticed that
foo.dat is checked out with a wrong attribute. You try to fix it like
this:
$ git reset HEAD^ .gitattributes
$ rm foo.dat
$ git checkout foo.dat
If you do not flip the direction, the one from the work tree is used which
is not what you want. If you do, then you break the other use case.
Either way, you cannot win.
In any case, I think I already queued the patch in 'pu' but without
documentation updates nor additional tests, so no need to include the
patch itself when you send in an update.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
2009-03-19 21:06 ` Junio C Hamano
@ 2009-03-20 8:11 ` Kristian Amlie
2009-03-20 9:32 ` [PATCH] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
0 siblings, 1 reply; 22+ messages in thread
From: Kristian Amlie @ 2009-03-20 8:11 UTC (permalink / raw)
To: ext Junio C Hamano; +Cc: git@vger.kernel.org
ext Junio C Hamano wrote:
> Kristian Amlie <kristian.amlie@nokia.com> writes:
>
>> ext Junio C Hamano wrote:
>> ...
>>> For example, you may notice that, after making a clean checkout, one path
>>> has a wrong attribute assigned to it, and may try to correct it. But how?
>>>
>>> $ edit .gitattributes ;# mark foo.dat as binary
>>> $ rm foo.dat
>>> $ git checkout foo.dat ;# make sure the new settings is correct???
>> As far as I can see, this works without any modifications to the patch.
>> Is that maybe because git_attr_set_direction() is not called if you use
>> that form of checkout?
>
> But that in itself can be seen as a bug, right? In another use case,
> suppose you botched your .gitattributes in HEAD version and noticed that
> foo.dat is checked out with a wrong attribute. You try to fix it like
> this:
>
> $ git reset HEAD^ .gitattributes
> $ rm foo.dat
> $ git checkout foo.dat
>
> If you do not flip the direction, the one from the work tree is used which
> is not what you want. If you do, then you break the other use case.
Right, I didn't even think about that case. My idea of gitattributes was
that the working tree copy is always the master version, and takes
precedence. The only reason that the index takes precedence in the "git
checkout <branch>" case is that there is no other way to get it checked
out correctly, so I see this as an implementation detail. I'm sure some
people would disagree though.
But you're right, there is no way to make both cases correct. From my
standpoint I'd say that the behavior of your patch is the most intuitive.
I'll follow up with the test case.
--
Kristian
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH] Add a test for checking whether gitattributes is honored by checkout.
2009-03-20 8:11 ` Kristian Amlie
@ 2009-03-20 9:32 ` Kristian Amlie
0 siblings, 0 replies; 22+ messages in thread
From: Kristian Amlie @ 2009-03-20 9:32 UTC (permalink / raw)
To: git; +Cc: Kristian Amlie
The original bug will not honor new entries in gitattributes if they
are changed in the same checkout as the files they affect.
It will also keep using .gitattributes, even if it is deleted in the
same commit as the files it affects.
---
t/t0020-crlf.sh | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 1be7446..4e72b53 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -429,6 +429,37 @@ test_expect_success 'in-tree .gitattributes (4)' '
}
'
+test_expect_success 'checkout with existing .gitattributes' '
+
+ git config core.autocrlf true &&
+ git config --unset core.safecrlf &&
+ echo ".file2 -crlfQ" | q_to_cr >> .gitattributes &&
+ git add .gitattributes &&
+ git commit -m initial &&
+ echo ".file -crlfQ" | q_to_cr >> .gitattributes &&
+ echo "contents" > .file &&
+ git add .gitattributes .file &&
+ git commit -m second &&
+
+ git checkout master~1 &&
+ git checkout master &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_success 'checkout when deleting .gitattributes' '
+
+ git rm .gitattributes &&
+ echo "contentsQ" | q_to_cr > .file2 &&
+ git add .file2 &&
+ git commit -m third
+
+ git checkout master~1 &&
+ git checkout master &&
+ remove_cr .file2 >/dev/null
+
+'
+
test_expect_success 'invalid .gitattributes (must not crash)' '
echo "three +crlf" >>.gitattributes &&
--
1.6.2.1.222.g570cc.dirty
^ permalink raw reply related [flat|nested] 22+ messages in thread
end of thread, other threads:[~2009-03-20 9:34 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-01-28 15:25 Honoring a checked out gitattributes file Kristian Amlie
2009-01-28 16:44 ` Michael J Gruber
2009-01-28 17:25 ` Kristian Amlie
2009-01-30 13:00 ` Kristian Amlie
2009-01-30 13:00 ` [PATCH] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
2009-03-12 9:36 ` Honoring a checked out gitattributes file Kristian Amlie
2009-03-12 9:36 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
2009-03-12 9:36 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
2009-03-12 9:59 ` Johannes Sixt
2009-03-12 10:23 ` Kristian Amlie
2009-03-13 13:24 ` Honoring a checked out gitattributes file Kristian Amlie
2009-03-13 13:24 ` [PATCH 1/2] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
2009-03-13 13:24 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout Kristian Amlie
2009-03-14 4:17 ` Junio C Hamano
2009-03-19 15:42 ` Kristian Amlie
2009-03-19 21:06 ` Junio C Hamano
2009-03-20 8:11 ` Kristian Amlie
2009-03-20 9:32 ` [PATCH] Add a test for checking whether gitattributes is honored by checkout Kristian Amlie
2009-03-14 4:36 ` [PATCH 1/2] " Junio C Hamano
2009-03-12 9:47 ` Matthieu Moy
2009-03-12 9:53 ` Kristian Amlie
2009-01-28 17:55 ` Honoring a checked out gitattributes file 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).