From mboxrd@z Thu Jan 1 00:00:00 1970 From: David Turner Subject: [PATCH] untracked-cache: fix subdirectory handling Date: Mon, 3 Aug 2015 18:37:23 -0400 Message-ID: <1438641443-3176-1-git-send-email-dturner@twopensource.com> Cc: David Turner To: git@vger.kernel.org, pclouds@gmail.com X-From: git-owner@vger.kernel.org Tue Aug 04 00:37:33 2015 Return-path: Envelope-to: gcvg-git-2@plane.gmane.org Received: from vger.kernel.org ([209.132.180.67]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZMOMO-0003Mh-QQ for gcvg-git-2@plane.gmane.org; Tue, 04 Aug 2015 00:37:33 +0200 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752269AbbHCWh2 (ORCPT ); Mon, 3 Aug 2015 18:37:28 -0400 Received: from mail-qg0-f53.google.com ([209.85.192.53]:32905 "EHLO mail-qg0-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751473AbbHCWh2 (ORCPT ); Mon, 3 Aug 2015 18:37:28 -0400 Received: by qged69 with SMTP id d69so99570977qge.0 for ; Mon, 03 Aug 2015 15:37:27 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=vqbBsvoCWzQh2hnfdAKHJUgQS8y+0zi+jc9Y139f2dg=; b=ZxuYNvjp/sQFui53L5CAOX2w6bbBxA7VVRiN+AGzPb232B7tKWvd7hBnM6zqsQzf3b Ru5n+yEuim9JjPX/83VrlTXsqSW+VRrxJ/bjeA3w6K4ANheiyK38kAKFUN+REDPiKbq7 yEwTpbjYhJK/13qEsl5bk59PpYnfun9aA2JkLLVFhNTQngxAqV0mK1cLIqFkvdJJBYBy lAHyPRgnwti9PKatU48HnMJ7Kpzy9BTcvMkE8rlTdj3qY8Nq7L2RB5ODAia3LWtE4+07 oO6c2SaIa9kLPwXm938U7x2oNAeATdjOyxneg8O4sjE4i76TBYhQngbFfHs2y2jq5UX8 Tuqg== X-Gm-Message-State: ALoCoQlwRphoQiow7PjIahBAXzti5R+IczkTAhRfjkfQ9qfrbsNuLcE7GbGpz2eCwE4WW6hNOVoy X-Received: by 10.140.97.72 with SMTP id l66mr708221qge.11.1438641447276; Mon, 03 Aug 2015 15:37:27 -0700 (PDT) Received: from ubuntu.jfk4.office.twttr.net ([192.133.79.147]) by smtp.gmail.com with ESMTPSA id l78sm3805622qkh.12.2015.08.03.15.37.25 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 03 Aug 2015 15:37:26 -0700 (PDT) X-Mailer: git-send-email 2.0.4.315.gad8727a-twtrsrc Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Archived-At: Previously, some calls lookup_untracked would pass a full path. But lookup_untracked assumes that the portion of the path up to and including to the untracked_cache_dir has been removed. So lookup_untracked would be looking in the untracked_cache for 'foo' for 'foo/bar' (instead of just looking for 'bar'). This would cause untracked cache corruption. To fix this, untracked_cache_dir learns to track its depth. Callers to lookup_untracked which have a full path now first trim off a sufficient number of path prefixes. A new helper function, lookup_untracked_recursive, helps untracked_cache_invalidate_path to perform this operation. Signed-off-by: David Turner --- This patch applies on top of dt/untracked-sparse, presently in `pu` at d2cd01bd. I think only the test part depends on that patch. Duy, let me know if you think this is the right approach. --- dir.c | 50 ++++++++++++++++++++------- dir.h | 1 + t/t7063-status-untracked-cache.sh | 72 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 13 deletions(-) diff --git a/dir.c b/dir.c index e7b89fe..314080b 100644 --- a/dir.c +++ b/dir.c @@ -631,6 +631,7 @@ static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc, memset(d, 0, sizeof(*d)); memcpy(d->name, name, len); d->name[len] = '\0'; + d->depth = dir->depth + 1; ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc); memmove(dir->dirs + first + 1, dir->dirs + first, @@ -1324,7 +1325,19 @@ static enum path_treatment treat_directory(struct dir_struct *dir, if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) return exclude ? path_excluded : path_untracked; - untracked = lookup_untracked(dir->untracked, untracked, dirname, len); + if (untracked) { + const char *cur = dirname; + int i; + + for (i = 0; i < untracked->depth; i++) { + cur = strchr(cur, '/'); + assert(cur); + cur++; + } + untracked = lookup_untracked(dir->untracked, untracked, + cur, + len - (cur - dirname)); + } return read_directory_recursive(dir, dirname, len, untracked, 1, simplify); } @@ -2431,7 +2444,7 @@ static void stat_data_from_disk(struct stat_data *to, const struct stat_data *fr } static int read_one_dir(struct untracked_cache_dir **untracked_, - struct read_data *rd) + struct read_data *rd, int depth) { struct untracked_cache_dir ud, *untracked; const unsigned char *next, *data = rd->data, *end = rd->end; @@ -2444,6 +2457,7 @@ static int read_one_dir(struct untracked_cache_dir **untracked_, value = decode_varint(&next); if (next > end) return -1; + ud.depth = depth; ud.recurse = 1; ud.untracked_alloc = value; ud.untracked_nr = value; @@ -2480,7 +2494,7 @@ static int read_one_dir(struct untracked_cache_dir **untracked_, rd->data = data; for (i = 0; i < untracked->dirs_nr; i++) { - len = read_one_dir(untracked->dirs + i, rd); + len = read_one_dir(untracked->dirs + i, rd, depth + 1); if (len < 0) return -1; } @@ -2577,7 +2591,7 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long rd.index = 0; rd.ucd = xmalloc(sizeof(*rd.ucd) * len); - if (read_one_dir(&uc->root, &rd) || rd.index != len) + if (read_one_dir(&uc->root, &rd, 0) || rd.index != len) goto done; next = rd.data; @@ -2614,20 +2628,32 @@ done2: return uc; } +static struct untracked_cache_dir *lookup_untracked_recursive( + struct untracked_cache *uc, struct untracked_cache_dir *dir, + const char *path, int len) +{ + const char *rest = strchr(path, '/'); + + if (rest) { + int component_len = rest - path; + struct untracked_cache_dir *d = + lookup_untracked(uc, dir, path, component_len); + return lookup_untracked_recursive(uc, d, rest + 1, + len - (component_len + 1)); + } else { + return dir; + } +} + void untracked_cache_invalidate_path(struct index_state *istate, const char *path) { - const char *sep; struct untracked_cache_dir *d; if (!istate->untracked || !istate->untracked->root) return; - sep = strrchr(path, '/'); - if (sep) - d = lookup_untracked(istate->untracked, - istate->untracked->root, - path, sep - path); - else - d = istate->untracked->root; + d = lookup_untracked_recursive(istate->untracked, + istate->untracked->root, + path, strlen(path)); istate->untracked->dir_invalidated++; d->valid = 0; d->untracked_nr = 0; diff --git a/dir.h b/dir.h index 7b5855d..885ed43 100644 --- a/dir.h +++ b/dir.h @@ -109,6 +109,7 @@ struct sha1_stat { * excludes_file_sha1[]) */ struct untracked_cache_dir { + unsigned int depth; struct untracked_cache_dir **dirs; char **untracked; struct stat_data stat_data; diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh index ff23f4e..bb0e2a9 100755 --- a/t/t7063-status-untracked-cache.sh +++ b/t/t7063-status-untracked-cache.sh @@ -408,7 +408,8 @@ test_expect_success 'set up sparse checkout' ' test_path_is_file done/one ' -test_expect_success 'create files, some of which are gitignored' ' +test_expect_success 'create/modify files, some of which are gitignored' ' + echo two bis >done/two && echo three >done/three && # three is gitignored echo four >done/four && # four is gitignored at a higher level echo five >done/five # five is not gitignored @@ -420,6 +421,7 @@ test_expect_success 'test sparse status with untracked cache' ' GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \ git status --porcelain >../status.actual && cat >../status.expect <../status.actual && cat >../status.expect < done/sub/sub/file +' + +test_expect_success 'test sparse status with untracked cache and subdir' ' + avoid_racy && + : >../trace && + GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \ + strace -k -o /tmp/st git status --porcelain >../status.actual && + cat >../status.expect <../trace.expect <../actual && + cat >../expect <../trace && + GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \ + git status --porcelain >../status.actual && + test_cmp ../status.expect ../status.actual && + cat >../trace.expect <