From: Kristian Amlie <kristian.amlie@nokia.com>
To: git@vger.kernel.org
Cc: Kristian Amlie <kristian.amlie@nokia.com>
Subject: [PATCH 2/2] Make Git respect changes to .gitattributes during checkout.
Date: Fri, 13 Mar 2009 14:24:16 +0100 [thread overview]
Message-ID: <1236950656-1967-3-git-send-email-kristian.amlie@nokia.com> (raw)
In-Reply-To: <1236950656-1967-2-git-send-email-kristian.amlie@nokia.com>
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
next prev parent reply other threads:[~2009-03-13 13:26 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Kristian Amlie [this message]
2009-03-14 4:17 ` [PATCH 2/2] Make Git respect changes to .gitattributes during checkout 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1236950656-1967-3-git-send-email-kristian.amlie@nokia.com \
--to=kristian.amlie@nokia.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).