[PATCH 17/24] untracked-cache: temporarily disable with $GIT_DISABLE_UNTRACKED_CACHE

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This can be used to double check if results with untracked cache are
correctly, compared to vanilla version. Untracked cache remains in
index, but not used.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dir.c b/dir.c
index 1c3db0b..5b9dd5d 100644
--- a/dir.c
+++ b/dir.c
@@ -1800,7 +1800,7 @@ static struct untracked_cache_dir 
*validate_untracked_cache(struct dir_struct *d
struct untracked_cache_dir *root;
int i;
 
-   if (!dir-untracked)
+   if (!dir-untracked || getenv(GIT_DISABLE_UNTRACKED_CACHE))
return NULL;
 
/*
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 24/24] git-status.txt: advertisement for untracked cache

2015-02-08 Thread Nguyễn Thái Ngọc Duy
When a good user sees the too long, consider -uno advice when
running `git status`, they should check out the man page to find out
more. This change suggests they try untracked cache before -uno.

Helped-by: brian m. carlson sand...@crustytoothpaste.net
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/git-status.txt | 5 -
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index def635f..4dcad4e 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -58,7 +58,10 @@ When `-u` option is not used, untracked files and 
directories are
 shown (i.e. the same as specifying `normal`), to help you avoid
 forgetting to add newly created files.  Because it takes extra work
 to find untracked files in the filesystem, this mode may take some
-time in a large working tree.  You can use `no` to have `git status`
+time in a large working tree.
+Consider enabling untracked cache and split index if supported (see
+`git update-index --untracked-cache` and `git update-index
+--split-index`), Otherwise you can use `no` to have `git status`
 return more quickly without showing untracked files.
 +
 The default can be changed using the status.showUntrackedFiles
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 19/24] update-index: manually enable or disable untracked cache

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Overall time saving on git status is about 40% in the best case
scenario, removing ..collect_untracked() as the most time consuming
function. read and refresh index operations are now at the top (which
should drop when index-helper and/or watchman support is added). More
numbers and analysis below.

webkit.git
==

169k files. 6k dirs. Lots of test data (i.e. not touched most of the
time)

Base status
---

Index version 4 in split index mode and cache-tree populated. No
untracked cache. It shows how time is consumed by git status. The
same settings are used for other repos below.

18:28:10.199679 builtin/commit.c:1394   performance: 0.00451 s: 
cmd_status:setup
18:28:10.474847 read-cache.c:1407   performance: 0.274873831 s: read_index
18:28:10.475295 read-cache.c:1407   performance: 0.00656 s: read_index
18:28:10.728443 preload-index.c:131 performance: 0.253147487 s: 
read_index_preload
18:28:10.741422 read-cache.c:1254   performance: 0.012868340 s: 
refresh_index
18:28:10.752300 wt-status.c:623 performance: 0.010421357 s: 
wt_status_collect_changes_worktree
18:28:10.762069 wt-status.c:629 performance: 0.009644748 s: 
wt_status_collect_changes_index
18:28:11.601019 wt-status.c:632 performance: 0.838859547 s: 
wt_status_collect_untracked
18:28:11.605939 builtin/commit.c:1421   performance: 0.004835004 s: 
cmd_status:update_index
18:28:11.606580 trace.c:415 performance: 1.407878388 s: git 
command: 'git' 'status'

Populating status
-

This is after enabling untracked cache and the cache is still empty.
We see a slight increase in .._collect_untracked() and update_index
(because new cache has to be written to $GIT_DIR/index).

18:28:18.915213 builtin/commit.c:1394   performance: 0.00326 s: 
cmd_status:setup
18:28:19.197364 read-cache.c:1407   performance: 0.281901416 s: read_index
18:28:19.197754 read-cache.c:1407   performance: 0.00546 s: read_index
18:28:19.451355 preload-index.c:131 performance: 0.253599607 s: 
read_index_preload
18:28:19.464400 read-cache.c:1254   performance: 0.012935336 s: 
refresh_index
18:28:19.475115 wt-status.c:623 performance: 0.010236920 s: 
wt_status_collect_changes_worktree
18:28:19.486022 wt-status.c:629 performance: 0.010801685 s: 
wt_status_collect_changes_index
18:28:20.362660 wt-status.c:632 performance: 0.876551366 s: 
wt_status_collect_untracked
18:28:20.396199 builtin/commit.c:1421   performance: 0.033447969 s: 
cmd_status:update_index
18:28:20.396939 trace.c:415 performance: 1.482695902 s: git 
command: 'git' 'status'

Populated status


After the cache is populated, wt_status_collect_untracked() drops 82%
from 0.838s to 0.144s. Overall time drops 45%. Top offenders are now
read_index() and read_index_preload().

18:28:20.408605 builtin/commit.c:1394   performance: 0.00457 s: 
cmd_status:setup
18:28:20.692864 read-cache.c:1407   performance: 0.283980458 s: read_index
18:28:20.693273 read-cache.c:1407   performance: 0.00661 s: read_index
18:28:20.958814 preload-index.c:131 performance: 0.265540254 s: 
read_index_preload
18:28:20.972375 read-cache.c:1254   performance: 0.013437429 s: 
refresh_index
18:28:20.983959 wt-status.c:623 performance: 0.011146646 s: 
wt_status_collect_changes_worktree
18:28:20.993948 wt-status.c:629 performance: 0.009879094 s: 
wt_status_collect_changes_index
18:28:21.138125 wt-status.c:632 performance: 0.144084737 s: 
wt_status_collect_untracked
18:28:21.173678 builtin/commit.c:1421   performance: 0.035463949 s: 
cmd_status:update_index
18:28:21.174251 trace.c:415 performance: 0.766707355 s: git 
command: 'git' 'status'

gentoo-x86.git
==

This repository is a strange one with a balanced, wide and shallow
worktree (about 100k files and 23k dirs) and no .gitignore in
worktree. .._collect_untracked() time drops 88%, total time drops 56%.

Base status
---
18:20:40.828642 builtin/commit.c:1394   performance: 0.00496 s: 
cmd_status:setup
18:20:41.027233 read-cache.c:1407   performance: 0.198130532 s: read_index
18:20:41.027670 read-cache.c:1407   performance: 0.00581 s: read_index
18:20:41.171716 preload-index.c:131 performance: 0.144045594 s: 
read_index_preload
18:20:41.179171 read-cache.c:1254   performance: 0.007320424 s: 
refresh_index
18:20:41.185785 wt-status.c:623 performance: 0.006144638 s: 
wt_status_collect_changes_worktree
18:20:41.192701 wt-status.c:629 performance: 0.006780184 s: 
wt_status_collect_changes_index
18:20:41.991723 wt-status.c:632 performance: 0.798927029 s: 
wt_status_collect_untracked
18:20:41.994664 builtin/commit.c:1421   performance: 0.002852772 s: 
cmd_status:update_index
18:20:41.995458 trace.c:415 performance: 1.168427502 s: git 
command: 'git' 'status'
Populating status
-
18:20:48.968848 

[PATCH 20/24] update-index: test the system before enabling untracked cache

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Helped-by: Eric Sunshine sunsh...@sunshineco.com
Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/git-update-index.txt |   6 ++
 builtin/update-index.c | 168 +
 2 files changed, 174 insertions(+)

diff --git a/Documentation/git-update-index.txt 
b/Documentation/git-update-index.txt
index f9a35cd..ed32bae 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -180,6 +180,12 @@ may not support it yet.
system must change `st_mtime` field of a directory if files
are added or deleted in that directory.
 
+--force-untracked-cache::
+   For safety, `--untracked-cache` performs tests on the working
+   directory to make sure untracked cache can be used. These
+   tests can take a few seconds. `--force-untracked-cache` can be
+   used to skip the tests.
+
 \--::
Do not interpret any more arguments as options.
 
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 3d2dedd..f5f6689 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -32,6 +32,7 @@ static int mark_valid_only;
 static int mark_skip_worktree_only;
 #define MARK_FLAG 1
 #define UNMARK_FLAG 2
+static struct strbuf mtime_dir = STRBUF_INIT;
 
 __attribute__((format (printf, 1, 2)))
 static void report(const char *fmt, ...)
@@ -47,6 +48,166 @@ static void report(const char *fmt, ...)
va_end(vp);
 }
 
+static void remove_test_directory(void)
+{
+   if (mtime_dir.len)
+   remove_dir_recursively(mtime_dir, 0);
+}
+
+static const char *get_mtime_path(const char *path)
+{
+   static struct strbuf sb = STRBUF_INIT;
+   strbuf_reset(sb);
+   strbuf_addf(sb, %s/%s, mtime_dir.buf, path);
+   return sb.buf;
+}
+
+static void xmkdir(const char *path)
+{
+   path = get_mtime_path(path);
+   if (mkdir(path, 0700))
+   die_errno(_(failed to create directory %s), path);
+}
+
+static int xstat_mtime_dir(struct stat *st)
+{
+   if (stat(mtime_dir.buf, st))
+   die_errno(_(failed to stat %s), mtime_dir.buf);
+   return 0;
+}
+
+static int create_file(const char *path)
+{
+   int fd;
+   path = get_mtime_path(path);
+   fd = open(path, O_CREAT | O_RDWR, 0644);
+   if (fd  0)
+   die_errno(_(failed to create file %s), path);
+   return fd;
+}
+
+static void xunlink(const char *path)
+{
+   path = get_mtime_path(path);
+   if (unlink(path))
+   die_errno(_(failed to delete file %s), path);
+}
+
+static void xrmdir(const char *path)
+{
+   path = get_mtime_path(path);
+   if (rmdir(path))
+   die_errno(_(failed to delete directory %s), path);
+}
+
+static void avoid_racy(void)
+{
+   /*
+* not use if we could usleep(10) if USE_NSEC is defined. The
+* field nsec could be there, but the OS could choose to
+* ignore it?
+*/
+   sleep(1);
+}
+
+static int test_if_untracked_cache_is_supported(void)
+{
+   struct stat st;
+   struct stat_data base;
+   int fd, ret = 0;
+
+   strbuf_addstr(mtime_dir, mtime-test-XX);
+   if (!mkdtemp(mtime_dir.buf))
+   die_errno(Could not make temporary directory);
+
+   fprintf(stderr, _(Testing ));
+   atexit(remove_test_directory);
+   xstat_mtime_dir(st);
+   fill_stat_data(base, st);
+   fputc('.', stderr);
+
+   avoid_racy();
+   fd = create_file(newfile);
+   xstat_mtime_dir(st);
+   if (!match_stat_data(base, st)) {
+   close(fd);
+   fputc('\n', stderr);
+   fprintf_ln(stderr,_(directory stat info does not 
+   change after adding a new file));
+   goto done;
+   }
+   fill_stat_data(base, st);
+   fputc('.', stderr);
+
+   avoid_racy();
+   xmkdir(new-dir);
+   xstat_mtime_dir(st);
+   if (!match_stat_data(base, st)) {
+   close(fd);
+   fputc('\n', stderr);
+   fprintf_ln(stderr, _(directory stat info does not change 
+after adding a new directory));
+   goto done;
+   }
+   fill_stat_data(base, st);
+   fputc('.', stderr);
+
+   avoid_racy();
+   write_or_die(fd, data, 4);
+   close(fd);
+   xstat_mtime_dir(st);
+   if (match_stat_data(base, st)) {
+   fputc('\n', stderr);
+   fprintf_ln(stderr, _(directory stat info changes 
+after updating a file));
+   goto done;
+   }
+   fputc('.', stderr);
+
+   avoid_racy();
+   close(create_file(new-dir/new));
+   xstat_mtime_dir(st);
+   if (match_stat_data(base, st)) {
+   fputc('\n', stderr);
+   fprintf_ln(stderr, _(directory stat info changes after 
+  

[PATCH 18/24] status: enable untracked cache

2015-02-08 Thread Nguyễn Thái Ngọc Duy
update_index_if_able() is moved down so that the updated untracked
cache could be written out.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 builtin/commit.c | 5 +++--
 wt-status.c  | 2 ++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 5ed6036..bdcfa61 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1372,13 +1372,14 @@ int cmd_status(int argc, const char **argv, const char 
*prefix)
refresh_index(the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, 
NULL, NULL);
 
fd = hold_locked_index(index_lock, 0);
-   if (0 = fd)
-   update_index_if_able(the_index, index_lock);
 
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.ignore_submodule_arg = ignore_submodule_arg;
wt_status_collect(s);
 
+   if (0 = fd)
+   update_index_if_able(the_index, index_lock);
+
if (s.relative_paths)
s.prefix = prefix;
 
diff --git a/wt-status.c b/wt-status.c
index 27da529..8880c3b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -585,6 +585,8 @@ static void wt_status_collect_untracked(struct wt_status *s)
DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
if (s-show_ignored_files)
dir.flags |= DIR_SHOW_IGNORED_TOO;
+   else
+   dir.untracked = the_index.untracked;
setup_standard_excludes(dir);
 
fill_directory(dir, s-pathspec);
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 23/24] untracked cache: guard and disable on system changes

2015-02-08 Thread Nguyễn Thái Ngọc Duy
If the user enables untracked cache, then

 - move worktree to an unsupported filesystem
 - or simply upgrade OS
 - or move the whole (portable) disk from one machine to another
 - or access a shared fs from another machine

there's no guarantee that untracked cache can still function properly.
Record the worktree location and OS footprint in the cache. If it
changes, err on the safe side and disable the cache. The user can
'update-index --untracked-cache' again to make sure all conditions are
met.

This adds a new requirement that setup_git_directory* must be called
before read_cache() because we need worktree location by then, or the
cache is dropped.

This change does not cover all bases, you can fool it if you try
hard. The point is to stop accidents.

Helped-by: Eric Sunshine sunsh...@sunshineco.com
Helped-by: brian m. carlson sand...@crustytoothpaste.net
Helped-by: Torsten Bögershausen tbo...@web.de
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/technical/index-format.txt |  4 +++
 builtin/update-index.c   | 16 ++
 dir.c| 55 +++-
 dir.h|  2 ++
 git-compat-util.h|  1 +
 test-dump-untracked-cache.c  |  1 +
 6 files changed, 72 insertions(+), 7 deletions(-)

diff --git a/Documentation/technical/index-format.txt 
b/Documentation/technical/index-format.txt
index 899dd3d..e24b4bc 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -242,6 +242,10 @@ Git index format
 
   The extension starts with
 
+  - A sequence of NUL-terminated strings, preceded by the size of the
+sequence in variable width encoding. Each string describes the
+environment where the cache can be used.
+
   - Stat data of $GIT_DIR/info/exclude. See Index entry section from
 ctime field until file size.
 
diff --git a/builtin/update-index.c b/builtin/update-index.c
index f5f6689..fc5e108 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -1103,7 +1103,7 @@ int cmd_update_index(int argc, const char **argv, const 
char *prefix)
the_index.split_index = NULL;
the_index.cache_changed |= SOMETHING_CHANGED;
}
-   if (untracked_cache  0  !the_index.untracked) {
+   if (untracked_cache  0) {
struct untracked_cache *uc;
 
if (untracked_cache  2) {
@@ -,11 +,15 @@ int cmd_update_index(int argc, const char **argv, const 
char *prefix)
if (!test_if_untracked_cache_is_supported())
return 1;
}
-   uc = xcalloc(1, sizeof(*uc));
-   uc-exclude_per_dir = .gitignore;
-   /* should be the same flags used by git-status */
-   uc-dir_flags = DIR_SHOW_OTHER_DIRECTORIES | 
DIR_HIDE_EMPTY_DIRECTORIES;
-   the_index.untracked = uc;
+   if (!the_index.untracked) {
+   uc = xcalloc(1, sizeof(*uc));
+   strbuf_init(uc-ident, 100);
+   uc-exclude_per_dir = .gitignore;
+   /* should be the same flags used by git-status */
+   uc-dir_flags = DIR_SHOW_OTHER_DIRECTORIES | 
DIR_HIDE_EMPTY_DIRECTORIES;
+   the_index.untracked = uc;
+   }
+   add_untracked_ident(the_index.untracked);
the_index.cache_changed |= UNTRACKED_CHANGED;
} else if (!untracked_cache  the_index.untracked) {
the_index.untracked = NULL;
diff --git a/dir.c b/dir.c
index 5b9dd5d..b8a4f9e 100644
--- a/dir.c
+++ b/dir.c
@@ -1793,6 +1793,40 @@ static int treat_leading_path(struct dir_struct *dir,
return rc;
 }
 
+static const char *get_ident_string(void)
+{
+   static struct strbuf sb = STRBUF_INIT;
+   struct utsname uts;
+
+   if (sb.len)
+   return sb.buf;
+   if (uname(uts))
+   die_errno(_(failed to get kernel name and information));
+   strbuf_addf(sb, Location %s, system %s %s %s, get_git_work_tree(),
+   uts.sysname, uts.release, uts.version);
+   return sb.buf;
+}
+
+static int ident_in_untracked(const struct untracked_cache *uc)
+{
+   const char *end = uc-ident.buf + uc-ident.len;
+   const char *p   = uc-ident.buf;
+
+   for (p = uc-ident.buf; p  end; p += strlen(p) + 1)
+   if (!strcmp(p, get_ident_string()))
+   return 1;
+   return 0;
+}
+
+void add_untracked_ident(struct untracked_cache *uc)
+{
+   if (ident_in_untracked(uc))
+   return;
+   strbuf_addstr(uc-ident, get_ident_string());
+   /* this strbuf contains a list of strings, save NUL too */
+   strbuf_addch(uc-ident, 0);
+}
+
 static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct 
*dir,
   

[PATCH 22/24] mingw32: add uname()

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Helped-by: Eric Sunshine sunsh...@sunshineco.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 compat/mingw.c | 11 +++
 compat/mingw.h |  9 +
 2 files changed, 20 insertions(+)

diff --git a/compat/mingw.c b/compat/mingw.c
index c5c37e5..88140e4 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2128,3 +2128,14 @@ void mingw_startup()
/* initialize Unicode console */
winansi_init();
 }
+
+int uname(struct utsname *buf)
+{
+   DWORD v = GetVersion();
+   memset(buf, 0, sizeof(*buf));
+   strcpy(buf-sysname, Windows);
+   sprintf(buf-release, %u.%u, v  0xff, (v  8)  0xff);
+   /* assuming NT variants only.. */
+   sprintf(buf-version, %u, (v  16)  0x7fff);
+   return 0;
+}
diff --git a/compat/mingw.h b/compat/mingw.h
index df0e320..d00ba7a 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -77,6 +77,14 @@ struct itimerval {
 };
 #define ITIMER_REAL 0
 
+struct utsname {
+   char sysname[16];
+   char nodename[1];
+   char release[16];
+   char version[16];
+   char machine[1];
+};
+
 /*
  * sanitize preprocessor namespace polluted by Windows headers defining
  * macros which collide with git local versions
@@ -166,6 +174,7 @@ struct passwd *getpwuid(uid_t uid);
 int setitimer(int type, struct itimerval *in, struct itimerval *out);
 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
 int link(const char *oldpath, const char *newpath);
+int uname(struct utsname *buf);
 
 /*
  * replacements of existing functions
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 21/24] t7063: tests for untracked cache

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 .gitignore |   1 +
 Makefile   |   1 +
 t/t7063-status-untracked-cache.sh (new +x) | 353 +
 test-dump-untracked-cache.c (new)  |  61 +
 4 files changed, 416 insertions(+)
 create mode 100755 t/t7063-status-untracked-cache.sh
 create mode 100644 test-dump-untracked-cache.c

diff --git a/.gitignore b/.gitignore
index 81e12c0..e2bb375 100644
--- a/.gitignore
+++ b/.gitignore
@@ -182,6 +182,7 @@
 /test-delta
 /test-dump-cache-tree
 /test-dump-split-index
+/test-dump-untracked-cache
 /test-scrap-cache-tree
 /test-genrandom
 /test-hashmap
diff --git a/Makefile b/Makefile
index 9f984a9..fa58a53 100644
--- a/Makefile
+++ b/Makefile
@@ -555,6 +555,7 @@ TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 TEST_PROGRAMS_NEED_X += test-dump-split-index
+TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 TEST_PROGRAMS_NEED_X += test-genrandom
 TEST_PROGRAMS_NEED_X += test-hashmap
 TEST_PROGRAMS_NEED_X += test-index-version
diff --git a/t/t7063-status-untracked-cache.sh 
b/t/t7063-status-untracked-cache.sh
new file mode 100755
index 000..2b2ffd7
--- /dev/null
+++ b/t/t7063-status-untracked-cache.sh
@@ -0,0 +1,353 @@
+#!/bin/sh
+
+test_description='test untracked cache'
+
+. ./test-lib.sh
+
+avoid_racy() {
+   sleep 1
+}
+
+git update-index --untracked-cache
+# It's fine if git update-index returns an error code other than one,
+# it'll be caught in the first test.
+if test $? -eq 1; then
+   skip_all='This system does not support untracked cache'
+   test_done
+fi
+
+test_expect_success 'setup' '
+   git init worktree 
+   cd worktree 
+   mkdir done dtwo dthree 
+   touch one two three done/one dtwo/two dthree/three 
+   git add one two done/one 
+   : .git/info/exclude 
+   git update-index --untracked-cache
+'
+
+test_expect_success 'untracked cache is empty' '
+   test-dump-untracked-cache ../actual 
+   cat ../expect EOF 
+info/exclude 
+core.excludesfile 
+exclude_per_dir .gitignore
+flags 0006
+EOF
+   test_cmp ../expect ../actual
+'
+
+cat ../status.expect EOF 
+A  done/one
+A  one
+A  two
+?? dthree/
+?? dtwo/
+?? three
+EOF
+
+cat ../dump.expect EOF 
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 
+exclude_per_dir .gitignore
+flags 0006
+/  recurse valid
+dthree/
+dtwo/
+three
+/done/  recurse valid
+/dthree/  recurse check_only valid
+three
+/dtwo/  recurse check_only valid
+two
+EOF
+
+test_expect_success 'status first time (empty cache)' '
+   avoid_racy 
+   : ../trace 
+   GIT_TRACE_UNTRACKED_STATS=$TRASH_DIRECTORY/trace \
+   git status --porcelain ../actual 
+   test_cmp ../status.expect ../actual 
+   cat ../trace.expect EOF 
+node creation: 3
+gitignore invalidation: 1
+directory invalidation: 0
+opendir: 4
+EOF
+   test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache after first status' '
+   test-dump-untracked-cache ../actual 
+   test_cmp ../dump.expect ../actual
+'
+
+test_expect_success 'status second time (fully populated cache)' '
+   avoid_racy 
+   : ../trace 
+   GIT_TRACE_UNTRACKED_STATS=$TRASH_DIRECTORY/trace \
+   git status --porcelain ../actual 
+   test_cmp ../status.expect ../actual 
+   cat ../trace.expect EOF 
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 0
+opendir: 0
+EOF
+   test_cmp ../trace.expect ../trace
+'
+
+test_expect_success 'untracked cache after second status' '
+   test-dump-untracked-cache ../actual 
+   test_cmp ../dump.expect ../actual
+'
+
+test_expect_success 'modify in root directory, one dir invalidation' '
+   avoid_racy 
+   : four 
+   : ../trace 
+   GIT_TRACE_UNTRACKED_STATS=$TRASH_DIRECTORY/trace \
+   git status --porcelain ../actual 
+   cat ../status.expect EOF 
+A  done/one
+A  one
+A  two
+?? dthree/
+?? dtwo/
+?? four
+?? three
+EOF
+   test_cmp ../status.expect ../actual 
+   cat ../trace.expect EOF 
+node creation: 0
+gitignore invalidation: 0
+directory invalidation: 1
+opendir: 1
+EOF
+   test_cmp ../trace.expect ../trace
+
+'
+
+test_expect_success 'verify untracked cache dump' '
+   test-dump-untracked-cache ../actual 
+   cat ../expect EOF 
+info/exclude e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+core.excludesfile 
+exclude_per_dir .gitignore
+flags 0006
+/  

[PATCH 12/24] untracked cache: invalidate at index addition or removal

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Ideally we should implement untracked_cache_remove_from_index() and
untracked_cache_add_to_index() so that they update untracked cache
right away instead of invalidating it and wait for read_directory()
next time to deal with it. But that may need some more work in
unpack-trees.c. So stay simple as the first step.

The new call in add_index_entry_with_check() may look strange because
new calls usually stay close to cache_tree_invalidate_path(). We do it
a bit later than c_t_i_p() in this function because if it's about
replacing the entry with the same name, we don't care (but cache-tree
does).

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c  | 31 +++
 dir.h  |  4 
 read-cache.c   |  4 
 unpack-trees.c |  7 +--
 4 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/dir.c b/dir.c
index e7d7df3..30e23f8 100644
--- a/dir.c
+++ b/dir.c
@@ -2495,3 +2495,34 @@ done2:
}
return uc;
 }
+
+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;
+   istate-untracked-dir_invalidated++;
+   d-valid = 0;
+   d-untracked_nr = 0;
+}
+
+void untracked_cache_remove_from_index(struct index_state *istate,
+  const char *path)
+{
+   untracked_cache_invalidate_path(istate, path);
+}
+
+void untracked_cache_add_to_index(struct index_state *istate,
+ const char *path)
+{
+   untracked_cache_invalidate_path(istate, path);
+}
diff --git a/dir.h b/dir.h
index 40a679a..2ce7dd3 100644
--- a/dir.h
+++ b/dir.h
@@ -298,6 +298,10 @@ static inline int dir_path_match(const struct dir_entry 
*ent,
  has_trailing_dir);
 }
 
+void untracked_cache_invalidate_path(struct index_state *, const char *);
+void untracked_cache_remove_from_index(struct index_state *, const char *);
+void untracked_cache_add_to_index(struct index_state *, const char *);
+
 void free_untracked_cache(struct untracked_cache *);
 struct untracked_cache *read_untracked_extension(const void *data, unsigned 
long sz);
 void write_untracked_extension(struct strbuf *out, struct untracked_cache 
*untracked);
diff --git a/read-cache.c b/read-cache.c
index 3736a56..d643a3f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -78,6 +78,7 @@ void rename_index_entry_at(struct index_state *istate, int 
nr, const char *new_n
memcpy(new-name, new_name, namelen + 1);
 
cache_tree_invalidate_path(istate, old-name);
+   untracked_cache_remove_from_index(istate, old-name);
remove_index_entry_at(istate, nr);
add_index_entry(istate, new, 
ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
@@ -537,6 +538,7 @@ int remove_file_from_index(struct index_state *istate, 
const char *path)
if (pos  0)
pos = -pos-1;
cache_tree_invalidate_path(istate, path);
+   untracked_cache_remove_from_index(istate, path);
while (pos  istate-cache_nr  !strcmp(istate-cache[pos]-name, 
path))
remove_index_entry_at(istate, pos);
return 0;
@@ -968,6 +970,8 @@ static int add_index_entry_with_check(struct index_state 
*istate, struct cache_e
}
pos = -pos-1;
 
+   untracked_cache_add_to_index(istate, ce-name);
+
/*
 * Inserting a merged entry (stage 0) into the index
 * will always replace all non-merged entries..
diff --git a/unpack-trees.c b/unpack-trees.c
index 629c658..e5ddb0c 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -9,6 +9,7 @@
 #include refs.h
 #include attr.h
 #include split-index.h
+#include dir.h
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -1255,8 +1256,10 @@ static int verify_uptodate_sparse(const struct 
cache_entry *ce,
 static void invalidate_ce_path(const struct cache_entry *ce,
   struct unpack_trees_options *o)
 {
-   if (ce)
-   cache_tree_invalidate_path(o-src_index, ce-name);
+   if (!ce)
+   return;
+   cache_tree_invalidate_path(o-src_index, ce-name);
+   untracked_cache_invalidate_path(o-src_index, ce-name);
 }
 
 /*
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 14/24] untracked cache: avoid racy timestamps

2015-02-08 Thread Nguyễn Thái Ngọc Duy
When a directory is updated within the same second that its timestamp
is last saved, we cannot realize the directory has been updated by
checking timestamps. Assume the worst (something is update). See
29e4d36 (Racy GIT - 2005-12-20) for more information.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 cache.h  | 2 ++
 dir.c| 4 ++--
 read-cache.c | 8 
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/cache.h b/cache.h
index b14d6e2..f8b3dc5 100644
--- a/cache.h
+++ b/cache.h
@@ -561,6 +561,8 @@ extern void fill_stat_data(struct stat_data *sd, struct 
stat *st);
  * INODE_CHANGED, and DATA_CHANGED.
  */
 extern int match_stat_data(const struct stat_data *sd, struct stat *st);
+extern int match_stat_data_racy(const struct index_state *istate,
+   const struct stat_data *sd, struct stat *st);
 
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
diff --git a/dir.c b/dir.c
index 30e23f8..439ff22 100644
--- a/dir.c
+++ b/dir.c
@@ -682,7 +682,7 @@ static int add_excludes(const char *fname, const char 
*base, int baselen,
if (sha1_stat) {
int pos;
if (sha1_stat-valid 
-   !match_stat_data(sha1_stat-stat, st))
+   !match_stat_data_racy(the_index, sha1_stat-stat, 
st))
; /* no content change, ss-sha1 still good */
else if (check_index 
 (pos = cache_name_pos(fname, strlen(fname))) 
= 0 
@@ -1538,7 +1538,7 @@ static int valid_cached_dir(struct dir_struct *dir,
return 0;
}
if (!untracked-valid ||
-   match_stat_data(untracked-stat_data, st)) {
+   match_stat_data_racy(the_index, untracked-stat_data, st)) {
if (untracked-valid)
invalidate_directory(dir-untracked, untracked);
fill_stat_data(untracked-stat_data, st);
diff --git a/read-cache.c b/read-cache.c
index f12a185..0ecba05 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -292,6 +292,14 @@ static int is_racy_timestamp(const struct index_state 
*istate,
is_racy_stat(istate, ce-ce_stat_data));
 }
 
+int match_stat_data_racy(const struct index_state *istate,
+const struct stat_data *sd, struct stat *st)
+{
+   if (is_racy_stat(istate, sd))
+   return MTIME_CHANGED;
+   return match_stat_data(sd, st);
+}
+
 int ie_match_stat(const struct index_state *istate,
  const struct cache_entry *ce, struct stat *st,
  unsigned int options)
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 15/24] untracked cache: print stats with $GIT_TRACE_UNTRACKED_STATS

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This could be used to verify correct behavior in tests

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/dir.c b/dir.c
index 439ff22..c5ca5ce 100644
--- a/dir.c
+++ b/dir.c
@@ -1922,6 +1922,18 @@ int read_directory(struct dir_struct *dir, const char 
*path, int len, const stru
free_simplify(simplify);
qsort(dir-entries, dir-nr, sizeof(struct dir_entry *), cmp_name);
qsort(dir-ignored, dir-ignored_nr, sizeof(struct dir_entry *), 
cmp_name);
+   if (dir-untracked) {
+   static struct trace_key trace_untracked_stats = 
TRACE_KEY_INIT(UNTRACKED_STATS);
+   trace_printf_key(trace_untracked_stats,
+node creation: %u\n
+gitignore invalidation: %u\n
+directory invalidation: %u\n
+opendir: %u\n,
+dir-untracked-dir_created,
+dir-untracked-gitignore_invalidated,
+dir-untracked-dir_invalidated,
+dir-untracked-dir_opened);
+   }
return dir-nr;
 }
 
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 11/24] untracked cache: load from UNTR index extension

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 dir.c| 220 +++
 dir.h|   2 +
 read-cache.c |   5 ++
 3 files changed, 227 insertions(+)

diff --git a/dir.c b/dir.c
index 1f2d701..e7d7df3 100644
--- a/dir.c
+++ b/dir.c
@@ -2275,3 +2275,223 @@ void write_untracked_extension(struct strbuf *out, 
struct untracked_cache *untra
strbuf_release(wd.sb_stat);
strbuf_release(wd.sb_sha1);
 }
+
+static void free_untracked(struct untracked_cache_dir *ucd)
+{
+   int i;
+   if (!ucd)
+   return;
+   for (i = 0; i  ucd-dirs_nr; i++)
+   free_untracked(ucd-dirs[i]);
+   for (i = 0; i  ucd-untracked_nr; i++)
+   free(ucd-untracked[i]);
+   free(ucd-untracked);
+   free(ucd-dirs);
+   free(ucd);
+}
+
+void free_untracked_cache(struct untracked_cache *uc)
+{
+   if (uc)
+   free_untracked(uc-root);
+   free(uc);
+}
+
+struct read_data {
+   int index;
+   struct untracked_cache_dir **ucd;
+   struct ewah_bitmap *check_only;
+   struct ewah_bitmap *valid;
+   struct ewah_bitmap *sha1_valid;
+   const unsigned char *data;
+   const unsigned char *end;
+};
+
+static void stat_data_from_disk(struct stat_data *to, const struct stat_data 
*from)
+{
+   to-sd_ctime.sec  = get_be32(from-sd_ctime.sec);
+   to-sd_ctime.nsec = get_be32(from-sd_ctime.nsec);
+   to-sd_mtime.sec  = get_be32(from-sd_mtime.sec);
+   to-sd_mtime.nsec = get_be32(from-sd_mtime.nsec);
+   to-sd_dev= get_be32(from-sd_dev);
+   to-sd_ino= get_be32(from-sd_ino);
+   to-sd_uid= get_be32(from-sd_uid);
+   to-sd_gid= get_be32(from-sd_gid);
+   to-sd_size   = get_be32(from-sd_size);
+}
+
+static int read_one_dir(struct untracked_cache_dir **untracked_,
+   struct read_data *rd)
+{
+#define NEXT(x) \
+   next = data + (x); \
+   if (next  rd-end) \
+   return -1;
+
+   struct untracked_cache_dir ud, *untracked;
+   const unsigned char *next, *data = rd-data, *end = rd-end;
+   unsigned int value;
+   int i, len;
+
+   memset(ud, 0, sizeof(ud));
+
+   next = data;
+   value = decode_varint(next);
+   if (next  end)
+   return -1;
+   ud.recurse = 1;
+   ud.untracked_alloc = value;
+   ud.untracked_nr= value;
+   if (ud.untracked_nr)
+   ud.untracked = xmalloc(sizeof(*ud.untracked) * ud.untracked_nr);
+   data = next;
+
+   next = data;
+   ud.dirs_alloc = ud.dirs_nr = decode_varint(next);
+   if (next  end)
+   return -1;
+   ud.dirs = xmalloc(sizeof(*ud.dirs) * ud.dirs_nr);
+   data = next;
+
+   len = strlen((const char *)data);
+   NEXT(len + 1);
+   *untracked_ = untracked = xmalloc(sizeof(*untracked) + len);
+   memcpy(untracked, ud, sizeof(ud));
+   memcpy(untracked-name, data, len + 1);
+   data = next;
+
+   for (i = 0; i  untracked-untracked_nr; i++) {
+   len = strlen((const char *)data);
+   NEXT(len + 1);
+   untracked-untracked[i] = xstrdup((const char*)data);
+   data = next;
+   }
+
+   rd-ucd[rd-index++] = untracked;
+   rd-data = data;
+
+   for (i = 0; i  untracked-dirs_nr; i++) {
+   len = read_one_dir(untracked-dirs + i, rd);
+   if (len  0)
+   return -1;
+   }
+   return 0;
+}
+
+static void set_check_only(size_t pos, void *cb)
+{
+   struct read_data *rd = cb;
+   struct untracked_cache_dir *ud = rd-ucd[pos];
+   ud-check_only = 1;
+}
+
+static void read_stat(size_t pos, void *cb)
+{
+   struct read_data *rd = cb;
+   struct untracked_cache_dir *ud = rd-ucd[pos];
+   if (rd-data + sizeof(struct stat_data)  rd-end) {
+   rd-data = rd-end + 1;
+   return;
+   }
+   stat_data_from_disk(ud-stat_data, (struct stat_data *)rd-data);
+   rd-data += sizeof(struct stat_data);
+   ud-valid = 1;
+}
+
+static void read_sha1(size_t pos, void *cb)
+{
+   struct read_data *rd = cb;
+   struct untracked_cache_dir *ud = rd-ucd[pos];
+   if (rd-data + 20  rd-end) {
+   rd-data = rd-end + 1;
+   return;
+   }
+   hashcpy(ud-exclude_sha1, rd-data);
+   rd-data += 20;
+}
+
+static void load_sha1_stat(struct sha1_stat *sha1_stat,
+  const struct stat_data *stat,
+  const unsigned char *sha1)
+{
+   stat_data_from_disk(sha1_stat-stat, stat);
+   hashcpy(sha1_stat-sha1, sha1);
+   sha1_stat-valid = 1;
+}
+
+struct untracked_cache *read_untracked_extension(const void *data, unsigned 
long sz)
+{
+   const struct ondisk_untracked_cache *ouc;
+   struct untracked_cache *uc;
+   struct read_data rd;
+   const 

[PATCH 06/24] untracked cache: record/validate dir mtime and reuse cached output

2015-02-08 Thread Nguyễn Thái Ngọc Duy
The main readdir loop in read_directory_recursive() is replaced with a
new one that checks if cached results of a directory is still valid.

If a file is added or removed from the index, the containing directory
is invalidated (but not its subdirs). If directory's mtime is changed,
the same happens. If a .gitignore is updated, the containing directory
and all subdirs are invalidated recursively. If dir_struct#flags or
other conditions change, the cache is ignored.

If a directory is invalidated, we opendir/readdir/closedir and run the
exclude machinery on that directory listing as usual. If untracked
cache is also enabled, we'll update the cache along the way. If a
directory is validated, we simply pull the untracked listing out from
the cache. The cache also records the list of direct subdirs that we
have to recurse in. Fully excluded directories are seen as untracked
files.

In the best case when no dirs are invalidated, read_directory()
becomes a series of

  stat(dir), open(.gitignore), fstat(), read(), close() and optionally
  hash_sha1_file()

For comparison, standard read_directory() is a sequence of

  opendir(), readdir(), open(.gitignore), fstat(), read(), close(), the
  expensive last_exclude_matching() and closedir().

We already try not to open(.gitignore) if we know it does not exist,
so open/fstat/read/close sequence does not apply to every
directory. The sequence could be reduced further, as noted in
prep_exclude() in another patch. So in theory, the entire best-case
read_directory sequence could be reduced to a series of stat() and
nothing else.

This is not a silver bullet approach. When you compile a C file, for
example, the old .o file is removed and a new one with the same name
created, effectively invalidating the containing directory's cache
(but not its subdirectories). If your build process touches every
directory, this cache adds extra overhead for nothing, so it's a good
idea to separate generated files from tracked files.. Editors may use
the same strategy for saving files. And of course you're out of luck
running your repo on an unsupported filesystem and/or operating system.

Helped-by: Eric Sunshine sunsh...@sunshineco.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 121 --
 dir.h |   2 ++
 2 files changed, 121 insertions(+), 2 deletions(-)

diff --git a/dir.c b/dir.c
index fb6ed86..8c989e3 100644
--- a/dir.c
+++ b/dir.c
@@ -37,7 +37,12 @@ enum path_treatment {
 struct cached_dir {
DIR *fdir;
struct untracked_cache_dir *untracked;
+   int nr_files;
+   int nr_dirs;
+
struct dirent *de;
+   const char *file;
+   struct untracked_cache_dir *ucd;
 };
 
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
@@ -607,6 +612,14 @@ static void invalidate_gitignore(struct untracked_cache 
*uc,
do_invalidate_gitignore(dir);
 }
 
+static void invalidate_directory(struct untracked_cache *uc,
+struct untracked_cache_dir *dir)
+{
+   uc-dir_invalidated++;
+   dir-valid = 0;
+   dir-untracked_nr = 0;
+}
+
 /*
  * Given a file with name fname, read it (either from disk, or from
  * the index if check_index is non-zero), parse it and store the
@@ -1424,6 +1437,39 @@ static enum path_treatment treat_one_path(struct 
dir_struct *dir,
}
 }
 
+static enum path_treatment treat_path_fast(struct dir_struct *dir,
+  struct untracked_cache_dir 
*untracked,
+  struct cached_dir *cdir,
+  struct strbuf *path,
+  int baselen,
+  const struct path_simplify *simplify)
+{
+   strbuf_setlen(path, baselen);
+   if (!cdir-ucd) {
+   strbuf_addstr(path, cdir-file);
+   return path_untracked;
+   }
+   strbuf_addstr(path, cdir-ucd-name);
+   /* treat_one_path() does this before it calls treat_directory() */
+   if (path-buf[path-len - 1] != '/')
+   strbuf_addch(path, '/');
+   if (cdir-ucd-check_only)
+   /*
+* check_only is set as a result of treat_directory() getting
+* to its bottom. Verify again the same set of directories
+* with check_only set.
+*/
+   return read_directory_recursive(dir, path-buf, path-len,
+   cdir-ucd, 1, simplify);
+   /*
+* We get path_recurse in the first run when
+* directory_exists_in_index() returns index_nonexistent. We
+* are sure that new changes in the index does not impact the
+* outcome. Return now.
+*/
+   return path_recurse;
+}
+
 static enum path_treatment treat_path(struct 

[PATCH 08/24] untracked cache: don't open non-existent .gitignore

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This cuts down a signficant number of open(.gitignore) because most
directories usually don't have .gitignore files.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 26 +-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/dir.c b/dir.c
index 02cdc26..95a0f0a 100644
--- a/dir.c
+++ b/dir.c
@@ -1019,7 +1019,21 @@ static void prep_exclude(struct dir_struct *dir, const 
char *base, int baselen)
/* Try to read per-directory file */
hashclr(sha1_stat.sha1);
sha1_stat.valid = 0;
-   if (dir-exclude_per_dir) {
+   if (dir-exclude_per_dir 
+   /*
+* If we know that no files have been added in
+* this directory (i.e. valid_cached_dir() has
+* been executed and set untracked-valid) ..
+*/
+   (!untracked || !untracked-valid ||
+/*
+ * .. and .gitignore does not exist before
+ * (i.e. null exclude_sha1 and skip_worktree is
+ * not set). Then we can skip loading .gitignore,
+ * which would result in ENOENT anyway.
+ * skip_worktree is taken care in read_directory()
+ */
+!is_null_sha1(untracked-exclude_sha1))) {
/*
 * dir-basebuf gets reused by the traversal, but we
 * need fname to remain unchanged to ensure the src
@@ -1782,6 +1796,7 @@ static struct untracked_cache_dir 
*validate_untracked_cache(struct dir_struct *d
  const struct pathspec 
*pathspec)
 {
struct untracked_cache_dir *root;
+   int i;
 
if (!dir-untracked)
return NULL;
@@ -1833,6 +1848,15 @@ static struct untracked_cache_dir 
*validate_untracked_cache(struct dir_struct *d
if (dir-exclude_list_group[EXC_CMDL].nr)
return NULL;
 
+   /*
+* An optimization in prep_exclude() does not play well with
+* CE_SKIP_WORKTREE. It's a rare case anyway, if a single
+* entry has that bit set, disable the whole untracked cache.
+*/
+   for (i = 0; i  active_nr; i++)
+   if (ce_skip_worktree(active_cache[i]))
+   return NULL;
+
if (!dir-untracked-root) {
const int len = sizeof(*dir-untracked-root);
dir-untracked-root = xmalloc(len);
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 07/24] untracked cache: mark what dirs should be recursed/saved

2015-02-08 Thread Nguyễn Thái Ngọc Duy
If we redo this thing in a functional style, we would have one struct
untracked_dir as input tree and another as output. The input is used
for verification. The output is a brand new tree, reflecting current
worktree.

But that means recreate a lot of dir nodes even if a lot could be
shared between input and output trees in good cases. So we go with the
messy but efficient way, combining both input and output trees into
one. We need a way to know which node in this combined tree belongs to
the output. This is the purpose of this recurse flag.

valid bit can't be used for this because it's about data of the node
except the subdirs. When we invalidate a directory, we want to keep
cached data of the subdirs intact even though we don't really know
what subdir still exists (yet). Then we check worktree to see what
actual subdir remains on disk. Those will have 'recurse' bit set
again. If cached data for those are still valid, we may be able to
avoid computing exclude files for them. Those subdirs that are deleted
will have 'recurse' remained clear and their 'valid' bits do not
matter.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 14 +-
 dir.h |  3 ++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/dir.c b/dir.c
index 8c989e3..02cdc26 100644
--- a/dir.c
+++ b/dir.c
@@ -615,9 +615,12 @@ static void invalidate_gitignore(struct untracked_cache 
*uc,
 static void invalidate_directory(struct untracked_cache *uc,
 struct untracked_cache_dir *dir)
 {
+   int i;
uc-dir_invalidated++;
dir-valid = 0;
dir-untracked_nr = 0;
+   for (i = 0; i  dir-dirs_nr; i++)
+   dir-dirs[i]-recurse = 0;
 }
 
 /*
@@ -1577,6 +1580,10 @@ static int read_cached_dir(struct cached_dir *cdir)
}
while (cdir-nr_dirs  cdir-untracked-dirs_nr) {
struct untracked_cache_dir *d = 
cdir-untracked-dirs[cdir-nr_dirs];
+   if (!d-recurse) {
+   cdir-nr_dirs++;
+   continue;
+   }
cdir-ucd = d;
cdir-nr_dirs++;
return 0;
@@ -1598,8 +1605,10 @@ static void close_cached_dir(struct cached_dir *cdir)
 * We have gone through this directory and found no untracked
 * entries. Mark it valid.
 */
-   if (cdir-untracked)
+   if (cdir-untracked) {
cdir-untracked-valid = 1;
+   cdir-untracked-recurse = 1;
+   }
 }
 
 /*
@@ -1842,6 +1851,9 @@ static struct untracked_cache_dir 
*validate_untracked_cache(struct dir_struct *d
invalidate_gitignore(dir-untracked, root);
dir-untracked-ss_excludes_file = dir-ss_excludes_file;
}
+
+   /* Make sure this directory is not dropped out at saving phase */
+   root-recurse = 1;
return root;
 }
 
diff --git a/dir.h b/dir.h
index ff3d99b..95baf01 100644
--- a/dir.h
+++ b/dir.h
@@ -115,8 +115,9 @@ struct untracked_cache_dir {
unsigned int untracked_alloc, dirs_nr, dirs_alloc;
unsigned int untracked_nr;
unsigned int check_only : 1;
-   /* all data in this struct are good */
+   /* all data except 'dirs' in this struct are good */
unsigned int valid : 1;
+   unsigned int recurse : 1;
/* null SHA-1 means this directory does not have .gitignore */
unsigned char exclude_sha1[20];
char name[FLEX_ARRAY];
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 00/24] nd/untracked-cache updates

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Code changes are all in 20/24, to avoid hard coding the test path. The
rest is documentation changes.

-- 8--
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 7850f53..4dcad4e 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -59,7 +59,7 @@ shown (i.e. the same as specifying `normal`), to help you 
avoid
 forgetting to add newly created files.  Because it takes extra work
 to find untracked files in the filesystem, this mode may take some
 time in a large working tree.
-Consider to enable untracked cache and split index if supported (see
+Consider enabling untracked cache and split index if supported (see
 `git update-index --untracked-cache` and `git update-index
 --split-index`), Otherwise you can use `no` to have `git status`
 return more quickly without showing untracked files.
diff --git a/Documentation/technical/index-format.txt 
b/Documentation/technical/index-format.txt
index 0045b89..e24b4bc 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -269,9 +269,9 @@ Git index format
   - A number of directory blocks in depth-first-search order, each
 consists of
 
-- The number of untracked entries, variable witdh encoding.
+- The number of untracked entries, variable width encoding.
 
-- The number of sub-directory blocks, variable with encoding.
+- The number of sub-directory blocks, variable width encoding.
 
 - The directory name terminated by NUL.
 
diff --git a/builtin/update-index.c b/builtin/update-index.c
index e76740d..fc5e108 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -32,6 +32,7 @@ static int mark_valid_only;
 static int mark_skip_worktree_only;
 #define MARK_FLAG 1
 #define UNMARK_FLAG 2
+static struct strbuf mtime_dir = STRBUF_INIT;
 
 __attribute__((format (printf, 1, 2)))
 static void report(const char *fmt, ...)
@@ -49,28 +50,37 @@ static void report(const char *fmt, ...)
 
 static void remove_test_directory(void)
 {
-   struct strbuf sb = STRBUF_INIT;
-   strbuf_addstr(sb, dir-mtime-test);
-   remove_dir_recursively(sb, 0);
-   strbuf_release(sb);
+   if (mtime_dir.len)
+   remove_dir_recursively(mtime_dir, 0);
+}
+
+static const char *get_mtime_path(const char *path)
+{
+   static struct strbuf sb = STRBUF_INIT;
+   strbuf_reset(sb);
+   strbuf_addf(sb, %s/%s, mtime_dir.buf, path);
+   return sb.buf;
 }
 
 static void xmkdir(const char *path)
 {
+   path = get_mtime_path(path);
if (mkdir(path, 0700))
die_errno(_(failed to create directory %s), path);
 }
 
-static int xstat(const char *path, struct stat *st)
+static int xstat_mtime_dir(struct stat *st)
 {
-   if (stat(path, st))
-   die_errno(_(failed to stat %s), path);
+   if (stat(mtime_dir.buf, st))
+   die_errno(_(failed to stat %s), mtime_dir.buf);
return 0;
 }
 
 static int create_file(const char *path)
 {
-   int fd = open(path, O_CREAT | O_RDWR, 0644);
+   int fd;
+   path = get_mtime_path(path);
+   fd = open(path, O_CREAT | O_RDWR, 0644);
if (fd  0)
die_errno(_(failed to create file %s), path);
return fd;
@@ -78,12 +88,14 @@ static int create_file(const char *path)
 
 static void xunlink(const char *path)
 {
+   path = get_mtime_path(path);
if (unlink(path))
die_errno(_(failed to delete file %s), path);
 }
 
 static void xrmdir(const char *path)
 {
+   path = get_mtime_path(path);
if (rmdir(path))
die_errno(_(failed to delete directory %s), path);
 }
@@ -102,37 +114,40 @@ static int test_if_untracked_cache_is_supported(void)
 {
struct stat st;
struct stat_data base;
-   int fd;
+   int fd, ret = 0;
+
+   strbuf_addstr(mtime_dir, mtime-test-XX);
+   if (!mkdtemp(mtime_dir.buf))
+   die_errno(Could not make temporary directory);
 
fprintf(stderr, _(Testing ));
-   xmkdir(dir-mtime-test);
atexit(remove_test_directory);
-   xstat(dir-mtime-test, st);
+   xstat_mtime_dir(st);
fill_stat_data(base, st);
fputc('.', stderr);
 
avoid_racy();
-   fd = create_file(dir-mtime-test/newfile);
-   xstat(dir-mtime-test, st);
+   fd = create_file(newfile);
+   xstat_mtime_dir(st);
if (!match_stat_data(base, st)) {
close(fd);
fputc('\n', stderr);
fprintf_ln(stderr,_(directory stat info does not 
change after adding a new file));
-   return 0;
+   goto done;
}
fill_stat_data(base, st);
fputc('.', stderr);
 
avoid_racy();
-   xmkdir(dir-mtime-test/new-dir);
-   xstat(dir-mtime-test, st);
+   xmkdir(new-dir);
+   xstat_mtime_dir(st);
if (!match_stat_data(base, st)) {

[PATCH 05/24] untracked cache: make a wrapper around {open,read,close}dir()

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This allows us to feed different info to read_directory_recursive()
based on untracked cache in the next patch.

Helped-by: Ramsay Jones ram...@ramsay1.demon.co.uk
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 55 +++
 1 file changed, 47 insertions(+), 8 deletions(-)

diff --git a/dir.c b/dir.c
index 6e91315..fb6ed86 100644
--- a/dir.c
+++ b/dir.c
@@ -31,6 +31,15 @@ enum path_treatment {
path_untracked
 };
 
+/*
+ * Support data structure for our opendir/readdir/closedir wrappers
+ */
+struct cached_dir {
+   DIR *fdir;
+   struct untracked_cache_dir *untracked;
+   struct dirent *de;
+};
+
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
const char *path, int len, struct untracked_cache_dir *untracked,
int check_only, const struct path_simplify *simplify);
@@ -1417,12 +1426,13 @@ static enum path_treatment treat_one_path(struct 
dir_struct *dir,
 
 static enum path_treatment treat_path(struct dir_struct *dir,
  struct untracked_cache_dir *untracked,
- struct dirent *de,
+ struct cached_dir *cdir,
  struct strbuf *path,
  int baselen,
  const struct path_simplify *simplify)
 {
int dtype;
+   struct dirent *de = cdir-de;
 
if (is_dot_or_dotdot(de-d_name) || !strcmp(de-d_name, .git))
return path_none;
@@ -1444,6 +1454,37 @@ static void add_untracked(struct untracked_cache_dir 
*dir, const char *name)
dir-untracked[dir-untracked_nr++] = xstrdup(name);
 }
 
+static int open_cached_dir(struct cached_dir *cdir,
+  struct dir_struct *dir,
+  struct untracked_cache_dir *untracked,
+  struct strbuf *path,
+  int check_only)
+{
+   memset(cdir, 0, sizeof(*cdir));
+   cdir-untracked = untracked;
+   cdir-fdir = opendir(path-len ? path-buf : .);
+   if (!cdir-fdir)
+   return -1;
+   return 0;
+}
+
+static int read_cached_dir(struct cached_dir *cdir)
+{
+   if (cdir-fdir) {
+   cdir-de = readdir(cdir-fdir);
+   if (!cdir-de)
+   return -1;
+   return 0;
+   }
+   return -1;
+}
+
+static void close_cached_dir(struct cached_dir *cdir)
+{
+   if (cdir-fdir)
+   closedir(cdir-fdir);
+}
+
 /*
  * Read a directory tree. We currently ignore anything but
  * directories, regular files and symlinks. That's because git
@@ -1460,23 +1501,21 @@ static enum path_treatment 
read_directory_recursive(struct dir_struct *dir,
struct untracked_cache_dir *untracked, int 
check_only,
const struct path_simplify *simplify)
 {
-   DIR *fdir;
+   struct cached_dir cdir;
enum path_treatment state, subdir_state, dir_state = path_none;
-   struct dirent *de;
struct strbuf path = STRBUF_INIT;
 
strbuf_add(path, base, baselen);
 
-   fdir = opendir(path.len ? path.buf : .);
-   if (!fdir)
+   if (open_cached_dir(cdir, dir, untracked, path, check_only))
goto out;
 
if (untracked)
untracked-check_only = !!check_only;
 
-   while ((de = readdir(fdir)) != NULL) {
+   while (!read_cached_dir(cdir)) {
/* check how the file or directory should be treated */
-   state = treat_path(dir, untracked, de, path, baselen, 
simplify);
+   state = treat_path(dir, untracked, cdir, path, baselen, 
simplify);
 
if (state  dir_state)
dir_state = state;
@@ -1529,7 +1568,7 @@ static enum path_treatment 
read_directory_recursive(struct dir_struct *dir,
break;
}
}
-   closedir(fdir);
+   close_cached_dir(cdir);
  out:
strbuf_release(path);
 
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 13/24] read-cache.c: split racy stat test to a separate function

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 read-cache.c | 24 +++-
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/read-cache.c b/read-cache.c
index d643a3f..f12a185 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -270,20 +270,26 @@ static int ce_match_stat_basic(const struct cache_entry 
*ce, struct stat *st)
return changed;
 }
 
-static int is_racy_timestamp(const struct index_state *istate,
-const struct cache_entry *ce)
+static int is_racy_stat(const struct index_state *istate,
+   const struct stat_data *sd)
 {
-   return (!S_ISGITLINK(ce-ce_mode) 
-   istate-timestamp.sec 
+   return (istate-timestamp.sec 
 #ifdef USE_NSEC
 /* nanosecond timestamped files can also be racy! */
-   (istate-timestamp.sec  ce-ce_stat_data.sd_mtime.sec ||
-(istate-timestamp.sec == ce-ce_stat_data.sd_mtime.sec 
- istate-timestamp.nsec = ce-ce_stat_data.sd_mtime.nsec))
+   (istate-timestamp.sec  sd-sd_mtime.sec ||
+(istate-timestamp.sec == sd-sd_mtime.sec 
+ istate-timestamp.nsec = sd-sd_mtime.nsec))
 #else
-   istate-timestamp.sec = ce-ce_stat_data.sd_mtime.sec
+   istate-timestamp.sec = sd-sd_mtime.sec
 #endif
-);
+   );
+}
+
+static int is_racy_timestamp(const struct index_state *istate,
+const struct cache_entry *ce)
+{
+   return (!S_ISGITLINK(ce-ce_mode) 
+   is_racy_stat(istate, ce-ce_stat_data));
 }
 
 int ie_match_stat(const struct index_state *istate,
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 16/24] untracked cache: mark index dirty if untracked cache is updated

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 cache.h  | 1 +
 dir.c| 9 +
 read-cache.c | 2 +-
 3 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/cache.h b/cache.h
index f8b3dc5..fca979b 100644
--- a/cache.h
+++ b/cache.h
@@ -295,6 +295,7 @@ static inline unsigned int canon_mode(unsigned int mode)
 #define RESOLVE_UNDO_CHANGED   (1  4)
 #define CACHE_TREE_CHANGED (1  5)
 #define SPLIT_INDEX_ORDERED(1  6)
+#define UNTRACKED_CHANGED  (1  7)
 
 struct split_index;
 struct untracked_cache;
diff --git a/dir.c b/dir.c
index c5ca5ce..1c3db0b 100644
--- a/dir.c
+++ b/dir.c
@@ -1933,6 +1933,15 @@ int read_directory(struct dir_struct *dir, const char 
*path, int len, const stru
 dir-untracked-gitignore_invalidated,
 dir-untracked-dir_invalidated,
 dir-untracked-dir_opened);
+   if (dir-untracked == the_index.untracked 
+   (dir-untracked-dir_opened ||
+dir-untracked-gitignore_invalidated ||
+dir-untracked-dir_invalidated))
+   the_index.cache_changed |= UNTRACKED_CHANGED;
+   if (dir-untracked != the_index.untracked) {
+   free(dir-untracked);
+   dir-untracked = NULL;
+   }
}
return dir-nr;
 }
diff --git a/read-cache.c b/read-cache.c
index 0ecba05..71d8e20 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -42,7 +42,7 @@ static struct cache_entry *refresh_cache_entry(struct 
cache_entry *ce,
 /* changes that can be kept in $GIT_DIR/index (basically all extensions) */
 #define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
 CE_ENTRY_ADDED | CE_ENTRY_REMOVED | CE_ENTRY_CHANGED | \
-SPLIT_INDEX_ORDERED)
+SPLIT_INDEX_ORDERED | UNTRACKED_CHANGED)
 
 struct index_state the_index;
 static const char *alternate_index_output;
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 10/24] untracked cache: save to an index extension

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/technical/index-format.txt |  58 +
 cache.h  |   3 +
 dir.c| 136 +++
 dir.h|   1 +
 read-cache.c |  12 +++
 5 files changed, 210 insertions(+)

diff --git a/Documentation/technical/index-format.txt 
b/Documentation/technical/index-format.txt
index fe6f316..899dd3d 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -233,3 +233,61 @@ Git index format
   The remaining index entries after replaced ones will be added to the
   final index. These added entries are also sorted by entry namme then
   stage.
+
+== Untracked cache
+
+  Untracked cache saves the untracked file list and necessary data to
+  verify the cache. The signature for this extension is { 'U', 'N',
+  'T', 'R' }.
+
+  The extension starts with
+
+  - Stat data of $GIT_DIR/info/exclude. See Index entry section from
+ctime field until file size.
+
+  - Stat data of core.excludesfile
+
+  - 32-bit dir_flags (see struct dir_struct)
+
+  - 160-bit SHA-1 of $GIT_DIR/info/exclude. Null SHA-1 means the file
+does not exist.
+
+  - 160-bit SHA-1 of core.excludesfile. Null SHA-1 means the file does
+not exist.
+
+  - NUL-terminated string of per-dir exclude file name. This usually
+is .gitignore.
+
+  - The number of following directory blocks, variable width
+encoding. If this number is zero, the extension ends here with a
+following NUL.
+
+  - A number of directory blocks in depth-first-search order, each
+consists of
+
+- The number of untracked entries, variable width encoding.
+
+- The number of sub-directory blocks, variable width encoding.
+
+- The directory name terminated by NUL.
+
+- A number of untrached file/dir names terminated by NUL.
+
+The remaining data of each directory block is grouped by type:
+
+  - An ewah bitmap, the n-th bit marks whether the n-th directory has
+valid untracked cache entries.
+
+  - An ewah bitmap, the n-th bit records check-only bit of
+read_directory_recursive() for the n-th directory.
+
+  - An ewah bitmap, the n-th bit indicates whether SHA-1 and stat data
+is valid for the n-th directory and exists in the next data.
+
+  - An array of stat data. The n-th data corresponds with the n-th
+one bit in the previous ewah bitmap.
+
+  - An array of SHA-1. The n-th SHA-1 corresponds with the n-th one bit
+in the previous ewah bitmap.
+
+  - One NUL.
diff --git a/cache.h b/cache.h
index dcf3a2a..b14d6e2 100644
--- a/cache.h
+++ b/cache.h
@@ -297,6 +297,8 @@ static inline unsigned int canon_mode(unsigned int mode)
 #define SPLIT_INDEX_ORDERED(1  6)
 
 struct split_index;
+struct untracked_cache;
+
 struct index_state {
struct cache_entry **cache;
unsigned int version;
@@ -310,6 +312,7 @@ struct index_state {
struct hashmap name_hash;
struct hashmap dir_hash;
unsigned char sha1[20];
+   struct untracked_cache *untracked;
 };
 
 extern struct index_state the_index;
diff --git a/dir.c b/dir.c
index 95a0f0a..1f2d701 100644
--- a/dir.c
+++ b/dir.c
@@ -12,6 +12,8 @@
 #include refs.h
 #include wildmatch.h
 #include pathspec.h
+#include varint.h
+#include ewah/ewok.h
 
 struct path_simplify {
int len;
@@ -2139,3 +2141,137 @@ void clear_directory(struct dir_struct *dir)
}
strbuf_release(dir-basebuf);
 }
+
+struct ondisk_untracked_cache {
+   struct stat_data info_exclude_stat;
+   struct stat_data excludes_file_stat;
+   uint32_t dir_flags;
+   unsigned char info_exclude_sha1[20];
+   unsigned char excludes_file_sha1[20];
+   char exclude_per_dir[FLEX_ARRAY];
+};
+
+#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, 
exclude_per_dir) + len + 1)
+
+struct write_data {
+   int index; /* number of written untracked_cache_dir */
+   struct ewah_bitmap *check_only; /* from untracked_cache_dir */
+   struct ewah_bitmap *valid;  /* from untracked_cache_dir */
+   struct ewah_bitmap *sha1_valid; /* set if exclude_sha1 is not null */
+   struct strbuf out;
+   struct strbuf sb_stat;
+   struct strbuf sb_sha1;
+};
+
+static void stat_data_to_disk(struct stat_data *to, const struct stat_data 
*from)
+{
+   to-sd_ctime.sec  = htonl(from-sd_ctime.sec);
+   to-sd_ctime.nsec = htonl(from-sd_ctime.nsec);
+   to-sd_mtime.sec  = htonl(from-sd_mtime.sec);
+   to-sd_mtime.nsec = htonl(from-sd_mtime.nsec);
+   to-sd_dev= htonl(from-sd_dev);
+   to-sd_ino= htonl(from-sd_ino);
+   to-sd_uid= htonl(from-sd_uid);
+   to-sd_gid= htonl(from-sd_gid);
+   to-sd_size   = htonl(from-sd_size);
+}
+
+static void write_one_dir(struct untracked_cache_dir *untracked,
+

[PATCH 09/24] ewah: add convenient wrapper ewah_serialize_strbuf()

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 ewah/ewah_io.c | 13 +
 ewah/ewok.h|  2 ++
 split-index.c  | 11 ++-
 3 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c
index 1c2d7af..43481b9 100644
--- a/ewah/ewah_io.c
+++ b/ewah/ewah_io.c
@@ -19,6 +19,7 @@
  */
 #include git-compat-util.h
 #include ewok.h
+#include strbuf.h
 
 int ewah_serialize_native(struct ewah_bitmap *self, int fd)
 {
@@ -110,6 +111,18 @@ int ewah_serialize(struct ewah_bitmap *self, int fd)
return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
 }
 
+static int write_strbuf(void *user_data, const void *data, size_t len)
+{
+   struct strbuf *sb = user_data;
+   strbuf_add(sb, data, len);
+   return len;
+}
+
+int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *sb)
+{
+   return ewah_serialize_to(self, write_strbuf, sb);
+}
+
 int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 {
const uint8_t *ptr = map;
diff --git a/ewah/ewok.h b/ewah/ewok.h
index f6ad190..4d7f5e9 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -30,6 +30,7 @@
 #  define ewah_calloc xcalloc
 #endif
 
+struct strbuf;
 typedef uint64_t eword_t;
 #define BITS_IN_WORD (sizeof(eword_t) * 8)
 
@@ -97,6 +98,7 @@ int ewah_serialize_to(struct ewah_bitmap *self,
  void *out);
 int ewah_serialize(struct ewah_bitmap *self, int fd);
 int ewah_serialize_native(struct ewah_bitmap *self, int fd);
+int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *);
 
 int ewah_deserialize(struct ewah_bitmap *self, int fd);
 int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
diff --git a/split-index.c b/split-index.c
index 21485e2..968b780 100644
--- a/split-index.c
+++ b/split-index.c
@@ -41,13 +41,6 @@ int read_link_extension(struct index_state *istate,
return 0;
 }
 
-static int write_strbuf(void *user_data, const void *data, size_t len)
-{
-   struct strbuf *sb = user_data;
-   strbuf_add(sb, data, len);
-   return len;
-}
-
 int write_link_extension(struct strbuf *sb,
 struct index_state *istate)
 {
@@ -55,8 +48,8 @@ int write_link_extension(struct strbuf *sb,
strbuf_add(sb, si-base_sha1, 20);
if (!si-delete_bitmap  !si-replace_bitmap)
return 0;
-   ewah_serialize_to(si-delete_bitmap, write_strbuf, sb);
-   ewah_serialize_to(si-replace_bitmap, write_strbuf, sb);
+   ewah_serialize_strbuf(si-delete_bitmap, sb);
+   ewah_serialize_strbuf(si-replace_bitmap, sb);
return 0;
 }
 
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 20/21] list-files: -M aka diff-cached

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 builtin/ls-files.c | 67 +++---
 1 file changed, 64 insertions(+), 3 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index d3540d1..b5e1a59 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -16,6 +16,9 @@
 #include pathspec.h
 #include color.h
 #include column.h
+#include diff.h
+#include diffcore.h
+#include revision.h
 
 static int abbrev;
 static int show_deleted;
@@ -25,6 +28,7 @@ static int show_stage;
 static int show_unmerged;
 static int show_resolve_undo;
 static int show_modified;
+static int show_diff_cached;
 static int show_killed;
 static int show_valid_bit;
 static int show_tag;
@@ -53,6 +57,7 @@ static const char *tag_removed = ;
 static const char *tag_other = ;
 static const char *tag_killed = ;
 static const char *tag_modified = ;
+static const char *tag_diff_cached = ;
 static const char *tag_skip_worktree = ;
 static const char *tag_resolve_undo = ;
 
@@ -412,7 +417,15 @@ static void show_files(struct dir_struct *dir)
err = lstat(ce-name, st);
if (show_deleted  err)
show_ce_entry(tag_removed, ce);
-   if (show_modified  ce_modified(ce, st, 0))
+   if (show_diff_cached  (ce-ce_flags  CE_MATCHED)) {
+   show_ce_entry(tag_diff_cached, ce);
+   /*
+* if we don't clear, it'll confuse 
write_ce_name()
+* when show_ce_entry(tag_modified, ce) is 
called
+*/
+   active_cache[i]-ce_flags = ~CE_MATCHED;
+   }
+   if (show_modified  (err || ce_modified(ce, st, 0)))
show_ce_entry(tag_modified, ce);
}
}
@@ -432,7 +445,8 @@ static void show_files_compact(struct dir_struct *dir)
if (show_killed)
show_killed_files(dir);
}
-   if (!(show_cached || show_unmerged || show_deleted || show_modified))
+   if (!(show_cached || show_unmerged || show_deleted ||
+ show_modified || show_diff_cached))
return;
for (i = 0; i  active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
@@ -452,6 +466,15 @@ static void show_files_compact(struct dir_struct *dir)
show_ce_entry(tag_removed, ce);
shown = 1;
}
+   if (show_diff_cached  (ce-ce_flags  CE_MATCHED)) {
+   show_ce_entry(tag_diff_cached, ce);
+   shown = 1;
+   /*
+* if we don't clear, it'll confuse write_ce_name()
+* when show_ce_entry(tag_modified, ce) is called
+*/
+   active_cache[i]-ce_flags = ~CE_MATCHED;
+   }
if (show_modified  (err || ce_modified(ce, st, 0))) {
show_ce_entry(tag_modified, ce);
shown = 1;
@@ -465,6 +488,38 @@ static void show_files_compact(struct dir_struct *dir)
}
 }
 
+static void mark_diff_cached(struct diff_queue_struct *q,
+struct diff_options *options,
+void *data)
+{
+   int i;
+
+   for (i = 0; i  q-nr; i++) {
+   struct diff_filepair *p = q-queue[i];
+   int pos = cache_name_pos(p-two-path, strlen(p-two-path));
+   if (pos  0)
+   continue;
+   active_cache[pos]-ce_flags |= CE_MATCHED;
+   }
+}
+
+static void diff_cached(struct pathspec *pathspec)
+{
+   struct rev_info rev;
+   const char *argv[] = { ls-files, HEAD, NULL };
+
+   init_revisions(rev, NULL);
+   setup_revisions(2, argv, rev, NULL);
+
+   rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+   rev.diffopt.format_callback = mark_diff_cached;
+   rev.diffopt.detect_rename = 1;
+   rev.diffopt.rename_limit = 200;
+   rev.diffopt.break_opt = 0;
+   copy_pathspec(rev.prune_data, pathspec);
+   run_diff_index(rev, 1);
+}
+
 /*
  * Prune the index to only contain stuff starting with prefix
  */
@@ -734,6 +789,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_(show cached files that are deleted on working 
directory)),
OPT_BOOL('m', modified, show_modified,
N_(show cached files that have modification on working 
directory)),
+   OPT_BOOL('M', modified, show_diff_cached,
+   N_(show modified files in the cache)),
OPT_BOOL('o', others, show_others,
N_(show untracked files)),

[PATCH 14/21] list-files: add -t back

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Tag H (cached) is not shown though because it's usually the majority
and becomes noise. Not showing it makes the other tags stand out. -t
is on by default if more than one file category is selected.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/git-list-files.txt |  6 ++
 builtin/ls-files.c   | 27 +--
 2 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 1c0c877..9d4c127 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -46,6 +46,12 @@ OPTIONS
 --unmerged::
Show unmerged files
 
+-t::
+--[no-]tag::
+   Show a tag to indicate file type. Automatically turned on with
+   multiple file selections. See linkgit::git-ls-files[1] option
+   `-t` for more information.
+
 -R::
 --recursive::
Equivalent of `--max-depth=-1` (infinite recursion).
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 62aa64f..0e7cbb9 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -596,6 +596,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_(show untracked files)),
OPT_SET_INT('R', recursive, max_depth,
N_(shortcut for --max-depth=-1), -1),
+   OPT_BOOL('t', tag, show_tag,
+   N_(identify the file status with tags)),
OPT_BIT('i', ignored, dir.flags,
N_(show ignored files),
DIR_SHOW_IGNORED),
@@ -636,6 +638,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
setup_standard_excludes(dir);
use_color = -1;
max_depth = 0;
+   show_tag = -1;
git_config(git_ls_config, NULL);
} else
git_config(git_default_config, NULL);
@@ -648,16 +651,6 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
for (i = 0; i  exclude_list.nr; i++) {
add_exclude(exclude_list.items[i].string, , 0, el, 
--exclude_args);
}
-   if (show_tag || show_valid_bit) {
-   tag_cached = H ;
-   tag_unmerged = M ;
-   tag_removed = R ;
-   tag_modified = C ;
-   tag_other = ? ;
-   tag_killed = K ;
-   tag_skip_worktree = S ;
-   tag_resolve_undo = U ;
-   }
if (show_modified || show_others || show_deleted || (dir.flags  
DIR_SHOW_IGNORED) || show_killed)
require_work_tree = 1;
if (show_unmerged  !porcelain)
@@ -711,6 +704,20 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
  show_killed || show_modified || show_resolve_undo))
show_cached = 1;
 
+   if (show_tag == -1)
+   show_tag = (show_cached + show_deleted + show_others +
+   show_unmerged + show_killed + show_modified)  1;
+   if (show_tag || show_valid_bit) {
+   tag_cached = porcelain ?: H ;
+   tag_unmerged = M ;
+   tag_removed = R ;
+   tag_modified = C ;
+   tag_other = ? ;
+   tag_killed = K ;
+   tag_skip_worktree = S ;
+   tag_resolve_undo = U ;
+   }
+
if (max_prefix)
prune_cache(max_prefix);
if (with_tree) {
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 21/21] t3080: tests for git-list-files

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Helped-by: Eric Sunshine sunsh...@sunshineco.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 t/t3080-list-files.sh (new +x) | 134 +
 1 file changed, 134 insertions(+)
 create mode 100755 t/t3080-list-files.sh

diff --git a/t/t3080-list-files.sh b/t/t3080-list-files.sh
new file mode 100755
index 000..01b9662
--- /dev/null
+++ b/t/t3080-list-files.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+test_description='git list-files test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+   mkdir dir 
+   touch file dir/file 
+   git init gitlink 
+   ( cd gitlink  test_commit foo ) 
+   git add file dir/file gitlink 
+   git commit -qm1
+'
+
+test_expect_success 'LS_COLORS env variable' '
+   LS_COLORS=rs=0:fi=31:di=32 \
+   git list-files --color=always | test_decode_color | \
+   grep -v gitlink actual 
+   cat expected -\EOF 
+   GREENdirRESET
+   REDfileRESET
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'color.ls.*' '
+   test_config color.ls.file red 
+   test_config color.ls.directory green 
+   test_config color.ls.submodule yellow 
+   git list-files --color=always | test_decode_color actual 
+   cat expected -\EOF 
+   GREENdirRESET
+   REDfileRESET
+   YELLOWgitlinkRESET
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'column output' '
+   COLUMNS=20 git list-files --column=always actual 
+   cat expected -\EOF 
+   dir  gitlink
+   file
+   EOF
+   test_cmp expected actual 
+   git list-files -1 actual 
+   cat expected -\EOF 
+   dir
+   file
+   gitlink
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success '--max-depth' '
+   git list-files --max-depth=1 actual 
+   cat expected -\EOF 
+   dir/file
+   file
+   gitlink
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'recursive' '
+   git list-files -R actual 
+   cat expected -\EOF 
+   dir/file
+   file
+   gitlink
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'globbing' '
+   git list-files f* actual 
+   cat expected -\EOF 
+   file
+   EOF
+   test_cmp expected actual 
+   git list-files **/f* actual 
+   cat expected -\EOF 
+   dir/file
+   file
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'no dups' '
+   test_when_finished git checkout file 
+   echo dirty file 
+   git list-files -m file actual 
+   echo file expected 
+   test_cmp expected actual 
+   git list-files -cm file actual 
+   echo C file expected 
+   test_cmp expected actual 
+   git list-files -tcm file actual 
+   test_cmp expected actual
+'
+
+test_expect_success '--classify' '
+   git list-files -F actual 
+   cat expected -\EOF 
+   dir/
+   file
+   gitlink
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'diff-cached' '
+   test_when_finished git checkout file 
+   echo dirty file 
+   git add file 
+   git list-files -M actual 
+   echo file expected 
+   test_cmp expected actual
+'
+
+test_expect_success 'unmerged files' '
+   git ls-files --stage file index-info 
+   sed s/ 0/ 2/;s/file/unmerged/ index-info | git update-index 
--index-info 
+   sed s/ 0/ 3/;s,file,dir/unmerged, index-info | git update-index 
--index-info 
+   git list-files -u actual 
+   cat expected -\EOF 
+   dir
+   unmerged
+   EOF
+   test_cmp expected actual
+'
+
+test_done
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 19/21] list-files -F: show submodules with the new indicator ''

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/git-list-files.txt | 4 ++--
 builtin/ls-files.c   | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 475c6da..223f6fd 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -54,8 +54,8 @@ OPTIONS
 
 -F::
 --classify::
-   Append indicator (one of `*/=@|`, which is executable,
-   directory, socket, Solaris door, symlink, or fifo
+   Append indicator (one of `*/=@|`, which is executable,
+   directory, socket, Solaris door, symlink, fifo, or submodule
respectively) to entries.
 
 -R::
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 9e444ff..d3540d1 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -92,6 +92,8 @@ static void append_indicator(struct strbuf *sb, mode_t mode)
c = '|';
else if (S_ISSOCK(mode))
c = '=';
+   else if (S_ISGITLINK(mode))
+   c = '';
 #ifdef S_ISDOOR
else if (S_ISDOOR(mode))
c = '';
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 18/21] list-files: add -F/--classify

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This appends an indicator after the file name if it's executable, a
directory and so on, like in GNU ls. In fact append_indicator() is a
rewrite from get_type_indicator() in coreutils.git commit
7326d1f1a67edf21947ae98194f98c38b6e9e527.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/git-list-files.txt |  6 ++
 builtin/ls-files.c   | 31 +++
 2 files changed, 37 insertions(+)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 9d4c127..475c6da 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -52,6 +52,12 @@ OPTIONS
multiple file selections. See linkgit::git-ls-files[1] option
`-t` for more information.
 
+-F::
+--classify::
+   Append indicator (one of `*/=@|`, which is executable,
+   directory, socket, Solaris door, symlink, or fifo
+   respectively) to entries.
+
 -R::
 --recursive::
Equivalent of `--max-depth=-1` (infinite recursion).
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 6be08fb..9e444ff 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -29,6 +29,7 @@ static int show_killed;
 static int show_valid_bit;
 static int show_tag;
 static int show_dirs;
+static int show_indicator;
 static int line_terminator = '\n';
 static int debug_mode;
 static int use_color;
@@ -77,6 +78,28 @@ static void write_name(struct strbuf *sb, const char *name)
quote_path_relative(name, real_prefix, sb);
 }
 
+static void append_indicator(struct strbuf *sb, mode_t mode)
+{
+   char c = 0;
+   if (S_ISREG(mode)) {
+   if (mode  (S_IXUSR | S_IXGRP | S_IXOTH))
+   c = '*';
+   } else if (S_ISDIR(mode))
+   c = '/';
+   else if (S_ISLNK(mode))
+   c = '@';
+   else if (S_ISFIFO(mode))
+   c = '|';
+   else if (S_ISSOCK(mode))
+   c = '=';
+#ifdef S_ISDOOR
+   else if (S_ISDOOR(mode))
+   c = '';
+#endif
+   if (c)
+   strbuf_addch(sb, c);
+}
+
 static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)
 {
if (column_active(colopts) || porcelain) {
@@ -99,6 +122,8 @@ static void write_dir_entry(struct strbuf *sb, const struct 
dir_entry *ent)
color_filename(sb, ent-name, quoted.buf, st.st_mode, 1);
else
strbuf_addbuf(sb, quoted);
+   if (show_indicator  st.st_mode)
+   append_indicator(sb, st.st_mode);
strbuf_addch(sb, line_terminator);
strbuf_release(quoted);
 }
@@ -202,6 +227,8 @@ static int show_as_directory(const struct cache_entry *ce)
}
if (show_tag)
strbuf_insert(sb2, 0, tag_cached, strlen(tag_cached));
+   if (show_indicator)
+   append_indicator(sb2, S_IFDIR);
strbuf_fputs(sb2, strbuf_detach(sb, NULL), NULL);
strbuf_release(sb2);
return 1;
@@ -218,6 +245,8 @@ static void write_ce_name(struct strbuf *sb, const struct 
cache_entry *ce)
color_filename(sb, ce-name, quoted.buf, ce-ce_mode, 1);
else
strbuf_addbuf(sb, quoted);
+   if (show_indicator)
+   append_indicator(sb, ce-ce_mode);
strbuf_addch(sb, line_terminator);
strbuf_release(quoted);
 }
@@ -714,6 +743,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
DIR_SHOW_IGNORED),
OPT_BOOL('u', unmerged, show_unmerged,
N_(show unmerged files)),
+   OPT_BOOL('F', classify, show_indicator,
+N_(append indicator (one of */=@|) to entries)),
OPT__COLOR(use_color, N_(show color)),
OPT_COLUMN(0, column, colopts, N_(show files in columns)),
OPT_SET_INT('1', NULL, colopts,
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 07/21] ls-files: add --column

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/git-ls-files.txt |  6 ++
 builtin/ls-files.c | 28 
 2 files changed, 34 insertions(+)

diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 148f226..99328b9 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -154,6 +154,12 @@ a space) at the start of each line:
`--color=never`. `--color` is equivalent to
`--color=auto`.
 
+--column[=options]::
+--no-column::
+   Display files in columns. See configuration variable column.ui
+   for option syntax. `--column` and `--no-column` without options
+   are equivalent to 'always' and 'never' respectively.
+
 \--::
Do not interpret any more arguments as options.
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 0ee4f19..44e5628 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -15,6 +15,7 @@
 #include string-list.h
 #include pathspec.h
 #include color.h
+#include column.h
 
 static int abbrev;
 static int show_deleted;
@@ -29,6 +30,7 @@ static int show_valid_bit;
 static int line_terminator = '\n';
 static int debug_mode;
 static int use_color;
+static unsigned int colopts;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -39,6 +41,7 @@ static char *ps_matched;
 static const char *with_tree;
 static int exc_given;
 static int exclude_args;
+static struct string_list output = STRING_LIST_INIT_NODUP;
 
 static const char *tag_cached = ;
 static const char *tag_unmerged = ;
@@ -66,6 +69,10 @@ static void write_name(struct strbuf *sb, const char *name)
 
 static void strbuf_fputs(struct strbuf *sb, FILE *fp)
 {
+   if (column_active(colopts)) {
+   string_list_append(output, strbuf_detach(sb, NULL));
+   return;
+   }
fwrite(sb-buf, sb-len, 1, fp);
 }
 
@@ -552,6 +559,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
OPT_STRING(0, with-tree, with_tree, N_(tree-ish),
N_(pretend that paths removed since tree-ish are 
still present)),
OPT__COLOR(use_color, N_(show color)),
+   OPT_COLUMN(0, column, colopts, N_(show files in columns)),
OPT__ABBREV(abbrev),
OPT_BOOL(0, debug, debug_mode, N_(show debugging data)),
OPT_END()
@@ -596,6 +604,18 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (dir.exclude_per_dir)
exc_given = 1;
 
+   finalize_colopts(colopts, -1);
+   if (explicitly_enable_column(colopts)) {
+   if (!line_terminator)
+   die(_(--column and -z are incompatible));
+   if (show_resolve_undo)
+   die(_(--column and --resolve-undo are incompatible));
+   if (debug_mode)
+   die(_(--column and --debug are incompatible));
+   }
+   if (column_active(colopts))
+   line_terminator = 0;
+
if (require_work_tree  !is_inside_work_tree())
setup_work_tree();
 
@@ -638,6 +658,14 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (show_resolve_undo)
show_ru_info();
 
+   if (column_active(colopts)) {
+   struct column_options copts;
+   memset(copts, 0, sizeof(copts));
+   copts.padding = 2;
+   print_columns(output, colopts, copts);
+   string_list_clear(output, 0);
+   }
+
if (ps_matched) {
int bad;
bad = report_path_error(ps_matched, pathspec, prefix);
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 11/21] list-files: -u does not imply showing stages

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Showing full index entry information is something for ls-files
only. The users of git list-files may just want to know what entries
are not unmerged.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 builtin/ls-files.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index d55b5e7..c5468c7 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -656,7 +656,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
}
if (show_modified || show_others || show_deleted || (dir.flags  
DIR_SHOW_IGNORED) || show_killed)
require_work_tree = 1;
-   if (show_unmerged)
+   if (show_unmerged  !porcelain)
/*
 * There's no point in showing unmerged unless
 * you also show the stage information.
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 09/21] list-files: a user friendly version of ls-files and more

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This is more user friendly version of ls-files:

 * it's automatically colored and columnized
 * it refreshes the index like all porcelain commands
 * it defaults to non-recursive behavior like ls
 * :(glob) is on by default so '*.c' means a.c but not a/b.c, use
   '**/*.c' for that.
 * auto pager

The name 'ls' is not taken. It is left for the user to make an alias
with better default options.

Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 .gitignore |  1 +
 Documentation/config.txt   | 10 +
 Documentation/git-list-files.txt (new) | 80 ++
 Makefile   |  1 +
 builtin/ls-files.c | 69 +++--
 command-list.txt   |  1 +
 git.c  |  1 +
 7 files changed, 159 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/git-list-files.txt

diff --git a/.gitignore b/.gitignore
index a052419..9727ecc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
+/git-list-files
 /git-log
 /git-ls-files
 /git-ls-remote
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2290c47..2dc4788 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -940,6 +940,12 @@ color.status.slot::
to red). The values of these variables may be specified as in
color.branch.slot.
 
+color.listFiles::
+   A boolean to enable/disable color in the output of
+   linkgit:git-list-files[1]. May be set to `always`, `false` (or
+   `never`) or `auto` (or `true`), in which case colors are used
+   only when the output is to a terminal. Defaults to false.
+
 color.ls.slot::
Use customized color for file name colorization. If not set
and the environment variable LS_COLORS is set, color settings
@@ -1012,6 +1018,10 @@ column.clean::
Specify the layout when list items in `git clean -i`, which always
shows files and directories in columns. See `column.ui` for details.
 
+column.listFiles::
+   Specify whether to output tag listing in `git list-files` in columns.
+   See `column.ui` for details.
+
 column.status::
Specify whether to output untracked files in `git status` in columns.
See `column.ui` for details.
diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
new file mode 100644
index 000..3039e1e
--- /dev/null
+++ b/Documentation/git-list-files.txt
@@ -0,0 +1,80 @@
+git-list-files(1)
+===
+
+NAME
+
+git-list-files - List files
+
+SYNOPSIS
+
+[verse]
+'git list-files [options] [pathspec...]
+
+DESCRIPTION
+---
+List files (by default in current working directory) that are in the
+index. Depending on the chosen options, maybe only modified files in
+working tree are shown, or untracked files...
+
+OPTIONS
+---
+-c::
+--cached::
+   Show cached files (default)
+
+-d::
+--deleted::
+   Show cached files that are deleted on working directory
+
+-m::
+--modified::
+   Show cached files that have modification on working directory
+
+-o::
+--others::
+   Show untracked files (and only unignored ones unless -i is
+   specified)
+
+-i::
+--ignored::
+   Show only ignored files. When showing files in the index,
+   print only those matched by an exclude pattern. When showing
+   other files, show only those matched by an exclude pattern.
+
+-u::
+--unmerged::
+   Show unmerged files
+
+--color[=when]::
+--no-color::
+   Color file names. The value must be `always`, `never`, or
+   `auto`. `--no-color` is equivalent to
+   `--color=never`. `--color` is equivalent to
+   `--color=auto`. See configuration variable `color.list-files`
+   for the default settings.
+
+--column[=options]::
+--no-column::
+   Display files in columns. See configuration variable column.ui
+   for option syntax. `--column` and `--no-column` without options
+   are equivalent to 'always' and 'never' respectively.
+
+--max-depth=depth::
+   For each pathspec given on command line, descend at most depth
+   levels of directories. A negative value means no limit.
+   This option is ignored if pathspec contains active wildcards.
+   In other words if a* matches a directory named a*,
+   * is matched literally so --max-depth is still effective.
+   The default is `--max-depth=0`.
+
+pathspec::
+   Files to show. :(glob) magic is enabled and recursion disabled
+   by default.
+
+SEE ALSO
+
+linkgit:git-ls-files[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Makefile b/Makefile
index 459121d..23a0751 100644
--- a/Makefile
+++ b/Makefile
@@ -587,6 +587,7 @@ BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-init$X

[PATCH 06/21] ls-files: add --color to highlight file names

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/git-ls-files.txt |  7 +++
 builtin/ls-files.c | 38 +++---
 2 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index e26f01f..148f226 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -147,6 +147,13 @@ a space) at the start of each line:
possible for manual inspection; the exact format may change at
any time.
 
+--color[=when]::
+--no-color::
+   Color file names. The value must be `always`, `never`, or
+   `auto`. `--no-color` is equivalent to
+   `--color=never`. `--color` is equivalent to
+   `--color=auto`.
+
 \--::
Do not interpret any more arguments as options.
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index b6f0d9f..0ee4f19 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -14,6 +14,7 @@
 #include resolve-undo.h
 #include string-list.h
 #include pathspec.h
+#include color.h
 
 static int abbrev;
 static int show_deleted;
@@ -27,6 +28,7 @@ static int show_killed;
 static int show_valid_bit;
 static int line_terminator = '\n';
 static int debug_mode;
+static int use_color;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -60,7 +62,6 @@ static void write_name(struct strbuf *sb, const char *name)
strbuf_release(sb2);
} else
quote_path_relative(name, real_prefix, sb);
-   strbuf_addch(sb, line_terminator);
 }
 
 static void strbuf_fputs(struct strbuf *sb, FILE *fp)
@@ -68,6 +69,21 @@ static void strbuf_fputs(struct strbuf *sb, FILE *fp)
fwrite(sb-buf, sb-len, 1, fp);
 }
 
+static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent)
+{
+   struct strbuf quoted = STRBUF_INIT;
+   struct stat st;
+   if (stat(ent-name, st))
+   st.st_mode = 0;
+   write_name(quoted, ent-name);
+   if (want_color(use_color))
+   color_filename(sb, ent-name, quoted.buf, st.st_mode, 1);
+   else
+   strbuf_addbuf(sb, quoted);
+   strbuf_addch(sb, line_terminator);
+   strbuf_release(quoted);
+}
+
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
static struct strbuf sb = STRBUF_INIT;
@@ -81,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry 
*ent)
 
strbuf_reset(sb);
strbuf_addstr(sb, tag);
-   write_name(sb, ent-name);
+   write_dir_entry(sb, ent);
strbuf_fputs(sb, stdout);
 }
 
@@ -146,6 +162,18 @@ static void show_killed_files(struct dir_struct *dir)
}
 }
 
+static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)
+{
+   struct strbuf quoted = STRBUF_INIT;
+   write_name(quoted, ce-name);
+   if (want_color(use_color))
+   color_filename(sb, ce-name, quoted.buf, ce-ce_mode, 1);
+   else
+   strbuf_addbuf(sb, quoted);
+   strbuf_addch(sb, line_terminator);
+   strbuf_release(quoted);
+}
+
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
static struct strbuf sb = STRBUF_INIT;
@@ -186,7 +214,7 @@ static void show_ce_entry(const char *tag, const struct 
cache_entry *ce)
find_unique_abbrev(ce-sha1, abbrev),
ce_stage(ce));
}
-   write_name(sb, ce-name);
+   write_ce_name(sb, ce);
strbuf_fputs(sb, stdout);
if (debug_mode) {
const struct stat_data *sd = ce-ce_stat_data;
@@ -523,6 +551,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_(if any file is not in the index, treat this as an 
error)),
OPT_STRING(0, with-tree, with_tree, N_(tree-ish),
N_(pretend that paths removed since tree-ish are 
still present)),
+   OPT__COLOR(use_color, N_(show color)),
OPT__ABBREV(abbrev),
OPT_BOOL(0, debug, debug_mode, N_(show debugging data)),
OPT_END()
@@ -570,6 +599,9 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (require_work_tree  !is_inside_work_tree())
setup_work_tree();
 
+   if (want_color(use_color))
+   parse_ls_color();
+
parse_pathspec(pathspec, 0,
   PATHSPEC_PREFER_CWD |
   PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 08/21] ls-files: support --max-depth

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/git-ls-files.txt | 7 +++
 builtin/ls-files.c | 7 +++
 2 files changed, 14 insertions(+)

diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 99328b9..3d921eb 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -160,6 +160,13 @@ a space) at the start of each line:
for option syntax. `--column` and `--no-column` without options
are equivalent to 'always' and 'never' respectively.
 
+--max-depth=depth::
+   For each pathspec given on command line, descend at most depth
+   levels of directories. A negative value means no limit (default).
+   This option is ignored if pathspec contains active wildcards.
+   In other words if a* matches a directory named a*,
+   * is matched literally so --max-depth is still effective.
+
 \--::
Do not interpret any more arguments as options.
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 44e5628..09a6b8d 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -503,6 +503,7 @@ static int option_parse_exclude_standard(const struct 
option *opt,
 int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 {
int require_work_tree = 0, show_tag = 0, i;
+   int max_depth = -1;
const char *max_prefix;
struct dir_struct dir;
struct exclude_list *el;
@@ -560,6 +561,9 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_(pretend that paths removed since tree-ish are 
still present)),
OPT__COLOR(use_color, N_(show color)),
OPT_COLUMN(0, column, colopts, N_(show files in columns)),
+   { OPTION_INTEGER, 0, max-depth, max_depth, N_(depth),
+   N_(descend at most depth levels), PARSE_OPT_NONEG,
+   NULL, 1 },
OPT__ABBREV(abbrev),
OPT_BOOL(0, debug, debug_mode, N_(show debugging data)),
OPT_END()
@@ -624,8 +628,11 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
 
parse_pathspec(pathspec, 0,
   PATHSPEC_PREFER_CWD |
+  (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) |
   PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
   prefix, argv);
+   pathspec.max_depth = max_depth;
+   pathspec.recursive = 1;
 
/* Find common prefix for all pathspec's */
max_prefix = common_prefix(pathspec);
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 15/21] list-files: sort output and remove duplicates

2015-02-08 Thread Nguyễn Thái Ngọc Duy
When you mix different file types, with ls-files you may get separate
listing. For example, ls-files -cm will show file abc twice: one
as part of cached list, one of modified list. With ls (and this
patch) they will be in a single sorted list (easier for the eye).

Duplicate entries are also removed. Note that display content is
compared, so if you have -t on, or you color file types differently,
you will get duplicate textual entries. This is good imo.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 builtin/ls-files.c | 36 
 1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 0e7cbb9..8f10ab9 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -53,6 +53,13 @@ static const char *tag_modified = ;
 static const char *tag_skip_worktree = ;
 static const char *tag_resolve_undo = ;
 
+static int compare_output(const void *a_, const void *b_)
+{
+   const struct string_list_item *a = a_;
+   const struct string_list_item *b = b_;
+   return strcmp(a-util, b-util);
+}
+
 static void write_name(struct strbuf *sb, const char *name)
 {
/*
@@ -68,10 +75,12 @@ static void write_name(struct strbuf *sb, const char *name)
quote_path_relative(name, real_prefix, sb);
 }
 
-static void strbuf_fputs(struct strbuf *sb, FILE *fp)
+static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp)
 {
-   if (column_active(colopts)) {
-   string_list_append(output, strbuf_detach(sb, NULL));
+   if (column_active(colopts) || porcelain) {
+   struct string_list_item *it;
+   it = string_list_append(output, strbuf_detach(sb, NULL));
+   it-util = (void *)full_name;
return;
}
fwrite(sb-buf, sb-len, 1, fp);
@@ -106,7 +115,7 @@ static void show_dir_entry(const char *tag, struct 
dir_entry *ent)
strbuf_reset(sb);
strbuf_addstr(sb, tag);
write_dir_entry(sb, ent);
-   strbuf_fputs(sb, stdout);
+   strbuf_fputs(sb, ent-name, stdout);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -223,7 +232,7 @@ static void show_ce_entry(const char *tag, const struct 
cache_entry *ce)
ce_stage(ce));
}
write_ce_name(sb, ce);
-   strbuf_fputs(sb, stdout);
+   strbuf_fputs(sb, ce-name, stdout);
if (debug_mode) {
const struct stat_data *sd = ce-ce_stat_data;
 
@@ -524,6 +533,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
const char *max_prefix;
struct dir_struct dir;
struct exclude_list *el;
+   struct column_options copts;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct option builtin_ls_files_options[] = {
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -671,7 +681,7 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (debug_mode)
die(_(--column and --debug are incompatible));
}
-   if (column_active(colopts))
+   if (column_active(colopts) || porcelain)
line_terminator = 0;
 
if (require_work_tree  !is_inside_work_tree())
@@ -737,13 +747,15 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
if (show_resolve_undo)
show_ru_info();
 
-   if (column_active(colopts)) {
-   struct column_options copts;
-   memset(copts, 0, sizeof(copts));
-   copts.padding = 2;
-   print_columns(output, colopts, copts);
-   string_list_clear(output, 0);
+   memset(copts, 0, sizeof(copts));
+   copts.padding = 2;
+   if (porcelain) {
+   qsort(output.items, output.nr, sizeof(*output.items),
+ compare_output);
+   string_list_remove_duplicates(output, 0);
}
+   print_columns(output, colopts, copts);
+   string_list_clear(output, 0);
 
if (ps_matched) {
int bad;
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 17/21] list-files: show directories as well as files

2015-02-08 Thread Nguyễn Thái Ngọc Duy
The index does not store directories explicitly (except submodules) so
we have to figure them out from file list when output lis depth-limited.

The function show_as_directory() deliberately generates duplicate
directories and expects the previous patch to remove duplicates.

Helped-by: Eric Sunshine sunsh...@sunshineco.com
Helped-by: Junio C Hamano gits...@pobox.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 builtin/ls-files.c | 64 ++
 1 file changed, 60 insertions(+), 4 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 457d067..6be08fb 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -27,6 +27,8 @@ static int show_resolve_undo;
 static int show_modified;
 static int show_killed;
 static int show_valid_bit;
+static int show_tag;
+static int show_dirs;
 static int line_terminator = '\n';
 static int debug_mode;
 static int use_color;
@@ -179,6 +181,35 @@ static void show_killed_files(struct dir_struct *dir)
}
 }
 
+static int show_as_directory(const struct cache_entry *ce)
+{
+   struct strbuf sb = STRBUF_INIT;
+   const char *p;
+
+   strbuf_add(sb, ce-name, ce_namelen(ce));
+   while (sb.len  (p = strrchr(sb.buf, '/')) != NULL) {
+   struct strbuf sb2 = STRBUF_INIT;
+   strbuf_setlen(sb, p - sb.buf);
+   if (!match_pathspec(pathspec, sb.buf, sb.len,
+   max_prefix_len, NULL, 1))
+   continue;
+   write_name(sb2, sb.buf);
+   if (want_color(use_color)) {
+   struct strbuf sb3 = STRBUF_INIT;
+   color_filename(sb3, ce-name, sb2.buf, S_IFDIR, 1);
+   strbuf_swap(sb2, sb3);
+   strbuf_release(sb3);
+   }
+   if (show_tag)
+   strbuf_insert(sb2, 0, tag_cached, strlen(tag_cached));
+   strbuf_fputs(sb2, strbuf_detach(sb, NULL), NULL);
+   strbuf_release(sb2);
+   return 1;
+   }
+   strbuf_release(sb);
+   return 0;
+}
+
 static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce)
 {
struct strbuf quoted = STRBUF_INIT;
@@ -191,17 +222,40 @@ static void write_ce_name(struct strbuf *sb, const struct 
cache_entry *ce)
strbuf_release(quoted);
 }
 
+static int match_pathspec_with_depth(struct pathspec *ps,
+const char *name, int namelen,
+int prefix, char *seen, int is_dir,
+const int *custom_depth)
+{
+   int saved_depth = ps-max_depth;
+   int result;
+
+   if (custom_depth)
+   ps-max_depth = *custom_depth;
+   result = match_pathspec(ps, name, namelen, prefix, seen, is_dir);
+   if (custom_depth)
+   ps-max_depth = saved_depth;
+   return result;
+}
+
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
static struct strbuf sb = STRBUF_INIT;
+   static const int infinite_depth = -1;
int len = max_prefix_len;
 
if (len = ce_namelen(ce))
die(git ls-files: internal error - cache entry not superset of 
prefix);
 
-   if (!match_pathspec(pathspec, ce-name, ce_namelen(ce),
-   len, ps_matched,
-   S_ISDIR(ce-ce_mode) || S_ISGITLINK(ce-ce_mode)))
+   if (!match_pathspec_with_depth(pathspec, ce-name, ce_namelen(ce),
+  len, ps_matched,
+  S_ISDIR(ce-ce_mode) || 
S_ISGITLINK(ce-ce_mode),
+  show_dirs ? infinite_depth : NULL))
+   return;
+
+   if (show_dirs strchr(ce-name, '/') 
+   !match_pathspec(pathspec, ce-name, ce_namelen(ce), prefix_len, 
NULL, 1) 
+   show_as_directory(ce))
return;
 
if (tag  *tag  show_valid_bit 
@@ -575,7 +629,7 @@ static int git_ls_config(const char *var, const char 
*value, void *cb)
 
 int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
 {
-   int require_work_tree = 0, show_tag = 0, i;
+   int require_work_tree = 0, i;
int max_depth = -1;
const char *max_prefix;
struct dir_struct dir;
@@ -744,6 +798,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
   prefix, argv);
pathspec.max_depth = max_depth;
pathspec.recursive = 1;
+   show_dirs = porcelain  max_depth != -1;
+
 
/* Find common prefix for all pathspec's */
max_prefix = common_prefix(pathspec);
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 16/21] list-files: do not show duplicate cached entries

2015-02-08 Thread Nguyễn Thái Ngọc Duy
With the current show_files() list-files -tcm will show

  foo.c
M foo.c

The first item is redundant. If foo.c is modified, we know it's in
the cache. Introduce show_files_compact to do that because ls-files is
plumbing and scripts may already depend on current display behavior.

Another difference in show_files_compact() is it does not show
skip-worktree (aka outside sparse checkout) entries anymore, which
makes sense in porcelain context.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 builtin/ls-files.c | 52 +++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 8f10ab9..457d067 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -333,6 +333,53 @@ static void show_files(struct dir_struct *dir)
}
 }
 
+static void show_files_compact(struct dir_struct *dir)
+{
+   int i;
+
+   /* For cached/deleted files we don't need to even do the readdir */
+   if (show_others || show_killed) {
+   if (!show_others)
+   dir-flags |= DIR_COLLECT_KILLED_ONLY;
+   fill_directory(dir, pathspec);
+   if (show_others)
+   show_other_files(dir);
+   if (show_killed)
+   show_killed_files(dir);
+   }
+   if (!(show_cached || show_unmerged || show_deleted || show_modified))
+   return;
+   for (i = 0; i  active_nr; i++) {
+   const struct cache_entry *ce = active_cache[i];
+   struct stat st;
+   int err, shown = 0;
+   if ((dir-flags  DIR_SHOW_IGNORED) 
+   !ce_excluded(dir, ce))
+   continue;
+   if (show_unmerged  !ce_stage(ce))
+   continue;
+   if (ce-ce_flags  CE_UPDATE)
+   continue;
+   if (ce_skip_worktree(ce))
+   continue;
+   err = lstat(ce-name, st);
+   if (show_deleted  err) {
+   show_ce_entry(tag_removed, ce);
+   shown = 1;
+   }
+   if (show_modified  (err || ce_modified(ce, st, 0))) {
+   show_ce_entry(tag_modified, ce);
+   shown = 1;
+   }
+   if (ce_stage(ce)) {
+   show_ce_entry(tag_unmerged, ce);
+   shown = 1;
+   }
+   if (!shown  show_cached)
+   show_ce_entry(tag_cached, ce);
+   }
+}
+
 /*
  * Prune the index to only contain stuff starting with prefix
  */
@@ -743,7 +790,10 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
refresh_index(the_index, REFRESH_QUIET | REFRESH_UNMERGED, 
pathspec, NULL, NULL);
setup_pager();
}
-   show_files(dir);
+   if (porcelain)
+   show_files_compact(dir);
+   else
+   show_files(dir);
if (show_resolve_undo)
show_ru_info();
 
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 13/21] list-files: add -1 short for --no-column

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/git-list-files.txt | 3 +++
 builtin/ls-files.c   | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 8d285c1..1c0c877 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -50,6 +50,9 @@ OPTIONS
 --recursive::
Equivalent of `--max-depth=-1` (infinite recursion).
 
+-1::
+   Equivalent of --no-column.
+
 --color[=when]::
 --no-color::
Color file names. The value must be `always`, `never`, or
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index adc31f6..62aa64f 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -603,6 +603,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_(show unmerged files)),
OPT__COLOR(use_color, N_(show color)),
OPT_COLUMN(0, column, colopts, N_(show files in columns)),
+   OPT_SET_INT('1', NULL, colopts,
+   N_(shortcut for --no-column), COL_PARSEOPT),
{ OPTION_INTEGER, 0, max-depth, max_depth, N_(depth),
N_(descend at most depth levels), PARSE_OPT_NONEG,
NULL, 1 },
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 05/21] ls-files: buffer full item in strbuf before printing

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Buffering so that we can manipulate the strings (e.g. coloring)
further before finally printing them.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 builtin/ls-files.c | 48 +++-
 1 file changed, 35 insertions(+), 13 deletions(-)

diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 99cee20..b6f0d9f 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -47,18 +47,30 @@ static const char *tag_modified = ;
 static const char *tag_skip_worktree = ;
 static const char *tag_resolve_undo = ;
 
-static void write_name(const char *name)
+static void write_name(struct strbuf *sb, const char *name)
 {
/*
 * With --full-name, prefix_len=0; this caller needs to pass
 * an empty string in that case (a NULL is good for ).
 */
-   write_name_quoted_relative(name, prefix_len ? prefix : NULL,
-  stdout, line_terminator);
+   const char *real_prefix = prefix_len ? prefix : NULL;
+   if (!line_terminator) {
+   struct strbuf sb2 = STRBUF_INIT;
+   strbuf_addstr(sb, relative_path(name, real_prefix, sb2));
+   strbuf_release(sb2);
+   } else
+   quote_path_relative(name, real_prefix, sb);
+   strbuf_addch(sb, line_terminator);
+}
+
+static void strbuf_fputs(struct strbuf *sb, FILE *fp)
+{
+   fwrite(sb-buf, sb-len, 1, fp);
 }
 
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
+   static struct strbuf sb = STRBUF_INIT;
int len = max_prefix_len;
 
if (len = ent-len)
@@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry 
*ent)
if (!dir_path_match(ent, pathspec, len, ps_matched))
return;
 
-   fputs(tag, stdout);
-   write_name(ent-name);
+   strbuf_reset(sb);
+   strbuf_addstr(sb, tag);
+   write_name(sb, ent-name);
+   strbuf_fputs(sb, stdout);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -134,6 +148,7 @@ static void show_killed_files(struct dir_struct *dir)
 
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
+   static struct strbuf sb = STRBUF_INIT;
int len = max_prefix_len;
 
if (len = ce_namelen(ce))
@@ -161,16 +176,18 @@ static void show_ce_entry(const char *tag, const struct 
cache_entry *ce)
tag = alttag;
}
 
+   strbuf_reset(sb);
if (!show_stage) {
-   fputs(tag, stdout);
+   strbuf_addstr(sb, tag);
} else {
-   printf(%s%06o %s %d\t,
-  tag,
-  ce-ce_mode,
-  find_unique_abbrev(ce-sha1,abbrev),
-  ce_stage(ce));
+   strbuf_addf(sb, %s%06o %s %d\t,
+   tag,
+   ce-ce_mode,
+   find_unique_abbrev(ce-sha1, abbrev),
+   ce_stage(ce));
}
-   write_name(ce-name);
+   write_name(sb, ce-name);
+   strbuf_fputs(sb, stdout);
if (debug_mode) {
const struct stat_data *sd = ce-ce_stat_data;
 
@@ -206,7 +223,12 @@ static void show_ru_info(void)
printf(%s%06o %s %d\t, tag_resolve_undo, ui-mode[i],
   find_unique_abbrev(ui-sha1[i], abbrev),
   i + 1);
-   write_name(path);
+   /*
+* With --full-name, prefix_len=0; this caller needs 
to pass
+* an empty string in that case (a NULL is good for ).
+*/
+   write_name_quoted_relative(path, prefix_len ? prefix : 
NULL,
+  stdout, line_terminator);
}
}
 }
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 02/21] ls_colors.c: parse color.ls.* from config file

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This is the second (and preferred) source for color information. This
will override $LS_COLORS.

Helped-by: Michael Blume blume.m...@gmail.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/config.txt | 11 +++
 ls_colors.c  | 30 +-
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 9220725..2090866 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -940,6 +940,17 @@ color.status.slot::
to red). The values of these variables may be specified as in
color.branch.slot.
 
+color.ls.slot::
+   Use customized color for file name colorization. If not set
+   and the environment variable LS_COLORS is set, color settings
+   from $LS_COLORS are used. `slot` can be `normal`, `file`,
+   `directory`, `symlink`, `fifo`, `socket`, `block`, `char`,
+   `missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,
+   `sticky`, `otherwritable`, `stickyotherwritable`, `cap`,
+   `multihardlink`. The values of these variables may be
+   specified as in color.branch.slot.
+
+
 color.ui::
This variable determines the default value for variables such
as `color.diff` and `color.grep` that control the use of color
diff --git a/ls_colors.c b/ls_colors.c
index e743315..2dc2d39 100644
--- a/ls_colors.c
+++ b/ls_colors.c
@@ -68,6 +68,14 @@ static const char *const indicator_name[] = {
NULL
 };
 
+static const char * const config_name[] = {
+   , , , , normal, file, directory, symlink,
+   fifo, socket, block, char, missing, orphan, executable,
+   door, setuid, setgid, sticky, otherwritable,
+   stickyotherwritable, cap, multihardlink, ,
+   NULL
+};
+
 struct bin_str {
size_t len; /* Number of bytes */
const char *string; /* Pointer to the same */
@@ -285,6 +293,23 @@ static int get_funky_string(char **dest, const char **src, 
int equals_end,
return state != ST_ERROR;
 }
 
+static int ls_colors_config(const char *var, const char *value, void *cb)
+{
+   int slot;
+   if (!starts_with(var, color.ls.))
+   return 0;
+   var += 9;
+   for (slot = 0; config_name[slot]; slot++)
+   if (!strcasecmp(var, config_name[slot]))
+   break;
+   if (!config_name[slot])
+   return 0;
+   if (!value)
+   return config_error_nonbool(var);
+   color_parse(value, ls_colors[slot]);
+   return 0;
+}
+
 void parse_ls_color(void)
 {
const char *p;  /* Pointer to character being parsed */
@@ -297,8 +322,10 @@ void parse_ls_color(void)
char *start;
size_t len;
 
-   if ((p = getenv(LS_COLORS)) == NULL || *p == '\0')
+   if ((p = getenv(LS_COLORS)) == NULL || *p == '\0') {
+   git_config(ls_colors_config, NULL);
return;
+   }
 
ext = NULL;
strcpy(label, ??);
@@ -395,4 +422,5 @@ void parse_ls_color(void)
 
if (!strcmp(ls_colors[LS_LN], target))
color_symlink_as_referent = 1;
+   git_config(ls_colors_config, NULL);
 }
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 03/21] ls_colors.c: add a function to color a file name

2015-02-08 Thread Nguyễn Thái Ngọc Duy
The new function is based on print_color_indicator() from commit
7326d1f1a67edf21947ae98194f98c38b6e9e527 in coreutils.git.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 color.h |  2 ++
 ls_colors.c | 66 +
 2 files changed, 68 insertions(+)

diff --git a/color.h b/color.h
index 3eaa5bd..b6904a3 100644
--- a/color.h
+++ b/color.h
@@ -94,5 +94,7 @@ void color_print_strbuf(FILE *fp, const char *color, const 
struct strbuf *sb);
 int color_is_nil(const char *color);
 
 void parse_ls_color(void);
+void color_filename(struct strbuf *sb, const char *name,
+   const char *display_name, mode_t mode, int linkok);
 
 #endif /* COLOR_H */
diff --git a/ls_colors.c b/ls_colors.c
index 2dc2d39..f84ef0f 100644
--- a/ls_colors.c
+++ b/ls_colors.c
@@ -424,3 +424,69 @@ void parse_ls_color(void)
color_symlink_as_referent = 1;
git_config(ls_colors_config, NULL);
 }
+
+void color_filename(struct strbuf *sb, const char *name,
+   const char *display_name, mode_t mode, int linkok)
+{
+   int type;
+   struct color_ext_type *ext; /* Color extension */
+
+   if (S_ISREG(mode)) {
+   type = LS_FL;
+   if ((mode  S_ISUID) != 0)
+   type = LS_SU;
+   else if ((mode  S_ISGID) != 0)
+   type = LS_SG;
+   else if ((mode  (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)
+   type = LS_EX;
+   } else if (S_ISDIR(mode)) {
+   if ((mode  S_ISVTX)  (mode  S_IWOTH))
+   type = LS_TW;
+   else if ((mode  S_IWOTH) != 0)
+   type = LS_OW;
+   else if ((mode  S_ISVTX) != 0)
+   type = LS_ST;
+   else
+   type = LS_DI;
+   } else if (S_ISLNK(mode))
+   type = (!linkok  *ls_colors[LS_OR]) ? LS_OR : LS_LN;
+   else if (S_ISFIFO(mode))
+   type = LS_PI;
+   else if (S_ISSOCK(mode))
+   type = LS_SO;
+   else if (S_ISBLK(mode))
+   type = LS_BD;
+   else if (S_ISCHR(mode))
+   type = LS_CD;
+#ifdef S_ISDOOR
+   else if (S_ISDOOR(mode))
+   type = LS_DO;
+#endif
+   else
+   /* Classify a file of some other type as C_ORPHAN.  */
+   type = LS_OR;
+
+   /* Check the file's suffix only if still classified as C_FILE.  */
+   ext = NULL;
+   if (type == LS_FL) {
+   /* Test if NAME has a recognized suffix.  */
+   size_t len = strlen(name);
+   const char *p = name + len; /* Pointer to final \0. 
 */
+   for (ext = color_ext_list; ext != NULL; ext = ext-next) {
+   if (ext-ext.len = len 
+   !strncmp(p - ext-ext.len, ext-ext.string, 
ext-ext.len))
+   break;
+   }
+   }
+
+   if (display_name)
+   name = display_name;
+   if (ext)
+   strbuf_addf(sb, \033[%.*sm%s%s,
+   (int)ext-seq.len, ext-seq.string,
+   name, GIT_COLOR_RESET);
+   else if (*ls_colors[type])
+   strbuf_addf(sb, %s%s%s, ls_colors[type], name, 
GIT_COLOR_RESET);
+   else
+   strbuf_addstr(sb, name);
+}
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 04/21] ls_colors.c: highlight submodules like directories

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/config.txt | 3 ++-
 ls_colors.c  | 8 +++-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2090866..2290c47 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -944,7 +944,8 @@ color.ls.slot::
Use customized color for file name colorization. If not set
and the environment variable LS_COLORS is set, color settings
from $LS_COLORS are used. `slot` can be `normal`, `file`,
-   `directory`, `symlink`, `fifo`, `socket`, `block`, `char`,
+   `directory`, `submodule`,
+   `symlink`, `fifo`, `socket`, `block`, `char`,
`missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`,
`sticky`, `otherwritable`, `stickyotherwritable`, `cap`,
`multihardlink`. The values of these variables may be
diff --git a/ls_colors.c b/ls_colors.c
index f84ef0f..011a8b9 100644
--- a/ls_colors.c
+++ b/ls_colors.c
@@ -29,6 +29,8 @@ enum color_ls {
LS_MH,  /* multi hardlink */
LS_CL,  /* clear end of line */
 
+   LS_SUBMODULE,
+
MAX_LS
 };
 
@@ -58,7 +60,8 @@ static char ls_colors[MAX_LS][COLOR_MAXLEN] = {
GIT_COLOR_BLACK_ON_GREEN,
,
,
-   
+   ,
+   GIT_COLOR_BOLD_BLUE
 };
 
 static const char *const indicator_name[] = {
@@ -73,6 +76,7 @@ static const char * const config_name[] = {
fifo, socket, block, char, missing, orphan, executable,
door, setuid, setgid, sticky, otherwritable,
stickyotherwritable, cap, multihardlink, ,
+   submodule,
NULL
 };
 
@@ -450,6 +454,8 @@ void color_filename(struct strbuf *sb, const char *name,
type = LS_DI;
} else if (S_ISLNK(mode))
type = (!linkok  *ls_colors[LS_OR]) ? LS_OR : LS_LN;
+   else if (S_ISGITLINK(mode))
+   type = LS_SUBMODULE;
else if (S_ISFIFO(mode))
type = LS_PI;
else if (S_ISSOCK(mode))
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 01/21] ls_colors.c: add $LS_COLORS parsing code

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Reusing color settings from $LS_COLORS could give a native look and
feel on file coloring.

This code is basically from coreutils.git [1], rewritten to fit Git.

As this is from GNU ls, the environment variable CLICOLOR is not
tested. It is to be decided later whether we should ignore $LS_COLORS
if $CLICOLOR is not set on Mac or FreeBSD.

[1] commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 file
src/ls.c. This is the last GPL-2 commit before coreutils turns to
GPL-3.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Makefile  |   1 +
 color.h   |   8 ++
 ls_colors.c (new) | 398 ++
 3 files changed, 407 insertions(+)
 create mode 100644 ls_colors.c

diff --git a/Makefile b/Makefile
index 827006b..459121d 100644
--- a/Makefile
+++ b/Makefile
@@ -703,6 +703,7 @@ LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
+LIB_OBJS += ls_colors.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge.o
diff --git a/color.h b/color.h
index f5beab1..3eaa5bd 100644
--- a/color.h
+++ b/color.h
@@ -45,6 +45,12 @@ struct strbuf;
 #define GIT_COLOR_BG_MAGENTA   \033[45m
 #define GIT_COLOR_BG_CYAN  \033[46m
 
+#define GIT_COLOR_WHITE_ON_RED\033[37;41m
+#define GIT_COLOR_WHITE_ON_BLUE   \033[37;44m
+#define GIT_COLOR_BLACK_ON_YELLOW \033[30;43m
+#define GIT_COLOR_BLUE_ON_GREEN   \033[34;42m
+#define GIT_COLOR_BLACK_ON_GREEN  \033[30;42m
+
 /* A special value meaning no color selected */
 #define GIT_COLOR_NIL NIL
 
@@ -87,4 +93,6 @@ void color_print_strbuf(FILE *fp, const char *color, const 
struct strbuf *sb);
 
 int color_is_nil(const char *color);
 
+void parse_ls_color(void);
+
 #endif /* COLOR_H */
diff --git a/ls_colors.c b/ls_colors.c
new file mode 100644
index 000..e743315
--- /dev/null
+++ b/ls_colors.c
@@ -0,0 +1,398 @@
+#include cache.h
+#include color.h
+
+enum color_ls {
+   LS_LC,  /* left, unused */
+   LS_RC,  /* right, unused */
+   LS_EC,  /* end color, unused */
+   LS_RS,  /* reset */
+   LS_NO,  /* normal */
+   LS_FL,  /* file, default */
+   LS_DI,  /* directory */
+   LS_LN,  /* symlink */
+
+   LS_PI,  /* pipe */
+   LS_SO,  /* socket */
+   LS_BD,  /* block device */
+   LS_CD,  /* char device */
+   LS_MI,  /* missing file */
+   LS_OR,  /* orphaned symlink */
+   LS_EX,  /* executable */
+   LS_DO,  /* Solaris door */
+
+   LS_SU,  /* setuid */
+   LS_SG,  /* setgid */
+   LS_ST,  /* sticky */
+   LS_OW,  /* other-writable */
+   LS_TW,  /* ow with sticky */
+   LS_CA,  /* cap */
+   LS_MH,  /* multi hardlink */
+   LS_CL,  /* clear end of line */
+
+   MAX_LS
+};
+
+static char ls_colors[MAX_LS][COLOR_MAXLEN] = {
+   ,
+   ,
+   ,
+   GIT_COLOR_RESET,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_BOLD_BLUE,
+   GIT_COLOR_BOLD_CYAN,
+
+   GIT_COLOR_YELLOW,
+   GIT_COLOR_BOLD_MAGENTA,
+   GIT_COLOR_BOLD_YELLOW,
+   GIT_COLOR_BOLD_YELLOW,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_NORMAL,
+   GIT_COLOR_BOLD_GREEN,
+   GIT_COLOR_BOLD_MAGENTA,
+
+   GIT_COLOR_WHITE_ON_RED,
+   GIT_COLOR_BLACK_ON_YELLOW,
+   GIT_COLOR_WHITE_ON_BLUE,
+   GIT_COLOR_BLUE_ON_GREEN,
+   GIT_COLOR_BLACK_ON_GREEN,
+   ,
+   ,
+   
+};
+
+static const char *const indicator_name[] = {
+   lc, rc, ec, rs, no, fi, di, ln,
+   pi, so, bd, cd, mi, or, ex, do,
+   su, sg, st, ow, tw, ca, mh, cl,
+   NULL
+};
+
+struct bin_str {
+   size_t len; /* Number of bytes */
+   const char *string; /* Pointer to the same */
+};
+
+struct color_ext_type {
+   struct bin_str ext; /* The extension we're looking for */
+   struct bin_str seq; /* The sequence to output when we do */
+   struct color_ext_type *next;/* Next in list */
+};
+
+static struct color_ext_type *color_ext_list;
+
+/*
+ * When true, in a color listing, color each symlink name according to the
+ * type of file it points to.  Otherwise, color them according to the `ln'
+ * directive in LS_COLORS.  Dangling (orphan) symlinks are treated specially,
+ * regardless.  This is set when `ln=target' appears in LS_COLORS.
+ */
+static int color_symlink_as_referent;
+
+/*
+ * Parse a string as part of the LS_COLORS variable; this may involve
+ * decoding all kinds of escape characters.  If equals_end is set an
+ * 

[PATCH 10/21] list-files: make alias 'ls' default to 'list-files'

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/git-list-files.txt | 3 ++-
 config.c | 8 
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 3039e1e..2182a38 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -14,7 +14,8 @@ DESCRIPTION
 ---
 List files (by default in current working directory) that are in the
 index. Depending on the chosen options, maybe only modified files in
-working tree are shown, or untracked files...
+working tree are shown, or untracked files... The builtin alias ls
+is set to list-files.
 
 OPTIONS
 ---
diff --git a/config.c b/config.c
index 15a2983..16209c6 100644
--- a/config.c
+++ b/config.c
@@ -40,6 +40,10 @@ static struct config_source *cf;
 
 static int zlib_compression_seen;
 
+static const char *builtin_config =
+   [alias]\n
+  ls = list-files\n;
+
 /*
  * Default config_set that contains key-value pairs from the usual set of 
config
  * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
@@ -1175,6 +1179,10 @@ int git_config_early(config_fn_t fn, void *data, const 
char *repo_config)
 
home_config_paths(user_config, xdg_config, config);
 
+   if (git_config_system())
+   git_config_from_buf(fn, builtin, builtin_config,
+   strlen(builtin_config), data);
+
if (git_config_system()  !access_or_die(git_etc_gitconfig(), R_OK, 
0)) {
ret += git_config_from_file(fn, git_etc_gitconfig(),
data);
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 12/21] list-files: add -R/--recursive short for --max-depth=-1

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 Documentation/git-list-files.txt | 4 
 builtin/ls-files.c   | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt
index 2182a38..8d285c1 100644
--- a/Documentation/git-list-files.txt
+++ b/Documentation/git-list-files.txt
@@ -46,6 +46,10 @@ OPTIONS
 --unmerged::
Show unmerged files
 
+-R::
+--recursive::
+   Equivalent of `--max-depth=-1` (infinite recursion).
+
 --color[=when]::
 --no-color::
Color file names. The value must be `always`, `never`, or
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index c5468c7..adc31f6 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -594,6 +594,8 @@ int cmd_ls_files(int argc, const char **argv, const char 
*cmd_prefix)
N_(show cached files that have modification on working 
directory)),
OPT_BOOL('o', others, show_others,
N_(show untracked files)),
+   OPT_SET_INT('R', recursive, max_depth,
+   N_(shortcut for --max-depth=-1), -1),
OPT_BIT('i', ignored, dir.flags,
N_(show ignored files),
DIR_SHOW_IGNORED),
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 02/24] untracked cache: record .gitignore information and dir hierarchy

2015-02-08 Thread Nguyễn Thái Ngọc Duy
The idea is if we can capture all input and (non-rescursive) output of
read_directory_recursive(), and can verify later that all the input is
the same, then the second r_d_r() should produce the same output as in
the first run.

The requirement for this to work is stat info of a directory MUST
change if an entry is added to or removed from that directory (and
should not change often otherwise). If your OS and filesystem do not
meet this requirement, untracked cache is not for you. Most file
systems on *nix should be fine. On Windows, NTFS is fine while FAT may
not be [1] even though FAT on Linux seems to be fine.

The list of input of r_d_r() is in the big comment block in dir.h. In
short, the output of a directory (not counting subdirs) mainly depends
on stat info of the directory in question, all .gitignore leading to
it and the check_only flag when r_d_r() is called recursively. This
patch records all this info (and the output) as r_d_r() runs.

Two hash_sha1_file() are required for $GIT_DIR/info/exclude and
core.excludesfile unless their stat data matches. hash_sha1_file() is
only needed when .gitignore files in the worktree are modified,
otherwise their SHA-1 in index is used (see the previous patch).

We could store stat data for .gitignore files so we don't have to
rehash them if their content is different from index, but I think
.gitignore files are rarely modified, so not worth extra cache data
(and hashing penalty read-cache.c:verify_hdr(), as we will be storing
this as an index extension).

The implication is, if you change .gitignore, you better add it to the
index soon or you lose all the benefit of untracked cache because a
modified .gitignore invalidates all subdirs recursively. This is
especially bad for .gitignore at root.

This cached output is about untracked files only, not ignored files
because the number of tracked files is usually small, so small cache
overhead, while the number of ignored files could go really high
(e.g. *.o files mixing with source code).

[1] Description of NTFS date and time stamps for files and folders
http://support.microsoft.com/kb/299648

Helped-by: Torsten Bögershausen tbo...@web.de
Helped-by: David Turner dtur...@twopensource.com
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 142 +-
 dir.h |  60 
 2 files changed, 183 insertions(+), 19 deletions(-)

diff --git a/dir.c b/dir.c
index 4cc936b..27734f0 100644
--- a/dir.c
+++ b/dir.c
@@ -32,7 +32,7 @@ enum path_treatment {
 };
 
 static enum path_treatment read_directory_recursive(struct dir_struct *dir,
-   const char *path, int len,
+   const char *path, int len, struct untracked_cache_dir *untracked,
int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
@@ -535,6 +535,54 @@ static void trim_trailing_spaces(char *buf)
 }
 
 /*
+ * Given a subdirectory name and dir of the current directory,
+ * search the subdir in dir and return it, or create a new one if it
+ * does not exist in dir.
+ *
+ * If name has the trailing slash, it'll be excluded in the search.
+ */
+static struct untracked_cache_dir *lookup_untracked(struct untracked_cache *uc,
+   struct untracked_cache_dir 
*dir,
+   const char *name, int len)
+{
+   int first, last;
+   struct untracked_cache_dir *d;
+   if (!dir)
+   return NULL;
+   if (len  name[len - 1] == '/')
+   len--;
+   first = 0;
+   last = dir-dirs_nr;
+   while (last  first) {
+   int cmp, next = (last + first)  1;
+   d = dir-dirs[next];
+   cmp = strncmp(name, d-name, len);
+   if (!cmp  strlen(d-name)  len)
+   cmp = -1;
+   if (!cmp)
+   return d;
+   if (cmp  0) {
+   last = next;
+   continue;
+   }
+   first = next+1;
+   }
+
+   uc-dir_created++;
+   d = xmalloc(sizeof(*d) + len + 1);
+   memset(d, 0, sizeof(*d));
+   memcpy(d-name, name, len);
+   d-name[len] = '\0';
+
+   ALLOC_GROW(dir-dirs, dir-dirs_nr + 1, dir-dirs_alloc);
+   memmove(dir-dirs + first + 1, dir-dirs + first,
+   (dir-dirs_nr - first) * sizeof(*dir-dirs));
+   dir-dirs_nr++;
+   dir-dirs[first] = d;
+   return d;
+}
+
+/*
  * Given a file with name fname, read it (either from disk, or from
  * the index if check_index is non-zero), parse it and store the
  * exclude rules in el.
@@ -645,14 +693,20 @@ struct exclude_list *add_exclude_list(struct dir_struct 
*dir,
 /*
  * Used to set up core.excludesfile and .git/info/exclude lists.
  */
-void add_excludes_from_file(struct dir_struct 

[PATCH 01/24] dir.c: optionally compute sha-1 of a .gitignore file

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This is not used anywhere yet. But the goal is to compare quickly if a
.gitignore file has changed when we have the SHA-1 of both old (cached
somewhere) and new (from index or a tree) versions.

Helped-by: Junio C Hamano gits...@pobox.com
Helped-by: Torsten Bögershausen tbo...@web.de
Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 53 ++---
 dir.h |  6 ++
 2 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/dir.c b/dir.c
index fcb6872..4cc936b 100644
--- a/dir.c
+++ b/dir.c
@@ -466,7 +466,8 @@ void add_exclude(const char *string, const char *base,
x-el = el;
 }
 
-static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
+static void *read_skip_worktree_file_from_index(const char *path, size_t *size,
+   struct sha1_stat *sha1_stat)
 {
int pos, len;
unsigned long sz;
@@ -485,6 +486,10 @@ static void *read_skip_worktree_file_from_index(const char 
*path, size_t *size)
return NULL;
}
*size = xsize_t(sz);
+   if (sha1_stat) {
+   memset(sha1_stat-stat, 0, sizeof(sha1_stat-stat));
+   hashcpy(sha1_stat-sha1, active_cache[pos]-sha1);
+   }
return data;
 }
 
@@ -529,11 +534,18 @@ static void trim_trailing_spaces(char *buf)
*last_space = '\0';
 }
 
-int add_excludes_from_file_to_list(const char *fname,
-  const char *base,
-  int baselen,
-  struct exclude_list *el,
-  int check_index)
+/*
+ * Given a file with name fname, read it (either from disk, or from
+ * the index if check_index is non-zero), parse it and store the
+ * exclude rules in el.
+ *
+ * If ss is not NULL, compute SHA-1 of the exclude file and fill
+ * stat data from disk (only valid if add_excludes returns zero). If
+ * ss_valid is non-zero, ss must contain good value as input.
+ */
+static int add_excludes(const char *fname, const char *base, int baselen,
+   struct exclude_list *el, int check_index,
+   struct sha1_stat *sha1_stat)
 {
struct stat st;
int fd, i, lineno = 1;
@@ -547,7 +559,7 @@ int add_excludes_from_file_to_list(const char *fname,
if (0 = fd)
close(fd);
if (!check_index ||
-   (buf = read_skip_worktree_file_from_index(fname, size)) == 
NULL)
+   (buf = read_skip_worktree_file_from_index(fname, size, 
sha1_stat)) == NULL)
return -1;
if (size == 0) {
free(buf);
@@ -560,6 +572,11 @@ int add_excludes_from_file_to_list(const char *fname,
} else {
size = xsize_t(st.st_size);
if (size == 0) {
+   if (sha1_stat) {
+   fill_stat_data(sha1_stat-stat, st);
+   hashcpy(sha1_stat-sha1, EMPTY_BLOB_SHA1_BIN);
+   sha1_stat-valid = 1;
+   }
close(fd);
return 0;
}
@@ -571,6 +588,21 @@ int add_excludes_from_file_to_list(const char *fname,
}
buf[size++] = '\n';
close(fd);
+   if (sha1_stat) {
+   int pos;
+   if (sha1_stat-valid 
+   !match_stat_data(sha1_stat-stat, st))
+   ; /* no content change, ss-sha1 still good */
+   else if (check_index 
+(pos = cache_name_pos(fname, strlen(fname))) 
= 0 
+!ce_stage(active_cache[pos]) 
+ce_uptodate(active_cache[pos]))
+   hashcpy(sha1_stat-sha1, 
active_cache[pos]-sha1);
+   else
+   hash_sha1_file(buf, size, blob, 
sha1_stat-sha1);
+   fill_stat_data(sha1_stat-stat, st);
+   sha1_stat-valid = 1;
+   }
}
 
el-filebuf = buf;
@@ -589,6 +621,13 @@ int add_excludes_from_file_to_list(const char *fname,
return 0;
 }
 
+int add_excludes_from_file_to_list(const char *fname, const char *base,
+  int baselen, struct exclude_list *el,
+  int check_index)
+{
+   return add_excludes(fname, base, baselen, el, check_index, NULL);
+}
+
 struct exclude_list *add_exclude_list(struct dir_struct *dir,
  int group_type, const char *src)
 {
diff --git a/dir.h b/dir.h
index 6c45e9d..cdca71b 100644
--- a/dir.h
+++ b/dir.h
@@ -73,6 +73,12 @@ struct 

[PATCH 03/24] untracked cache: initial untracked cache validation

2015-02-08 Thread Nguyễn Thái Ngọc Duy
Make sure the starting conditions and all global exclude files are
good to go. If not, either disable untracked cache completely, or wipe
out the cache and start fresh.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 113 --
 dir.h |   4 +++
 2 files changed, 114 insertions(+), 3 deletions(-)

diff --git a/dir.c b/dir.c
index 27734f0..44ed9f2 100644
--- a/dir.c
+++ b/dir.c
@@ -582,6 +582,22 @@ static struct untracked_cache_dir *lookup_untracked(struct 
untracked_cache *uc,
return d;
 }
 
+static void do_invalidate_gitignore(struct untracked_cache_dir *dir)
+{
+   int i;
+   dir-valid = 0;
+   dir-untracked_nr = 0;
+   for (i = 0; i  dir-dirs_nr; i++)
+   do_invalidate_gitignore(dir-dirs[i]);
+}
+
+static void invalidate_gitignore(struct untracked_cache *uc,
+struct untracked_cache_dir *dir)
+{
+   uc-gitignore_invalidated++;
+   do_invalidate_gitignore(dir);
+}
+
 /*
  * Given a file with name fname, read it (either from disk, or from
  * the index if check_index is non-zero), parse it and store the
@@ -697,6 +713,13 @@ static void add_excludes_from_file_1(struct dir_struct 
*dir, const char *fname,
 struct sha1_stat *sha1_stat)
 {
struct exclude_list *el;
+   /*
+* catch setup_standard_excludes() that's called before
+* dir-untracked is assigned. That function behaves
+* differently when dir-untracked is non-NULL.
+*/
+   if (!dir-untracked)
+   dir-unmanaged_exclude_files++;
el = add_exclude_list(dir, EXC_FILE, fname);
if (add_excludes(fname, , 0, el, 0, sha1_stat)  0)
die(cannot use %s as an exclude file, fname);
@@ -704,6 +727,7 @@ static void add_excludes_from_file_1(struct dir_struct 
*dir, const char *fname,
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
+   dir-unmanaged_exclude_files++; /* see validate_untracked_cache() */
add_excludes_from_file_1(dir, fname, NULL);
 }
 
@@ -1572,9 +1596,87 @@ static int treat_leading_path(struct dir_struct *dir,
return rc;
 }
 
+static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct 
*dir,
+ int base_len,
+ const struct pathspec 
*pathspec)
+{
+   struct untracked_cache_dir *root;
+
+   if (!dir-untracked)
+   return NULL;
+
+   /*
+* We only support $GIT_DIR/info/exclude and core.excludesfile
+* as the global ignore rule files. Any other additions
+* (e.g. from command line) invalidate the cache. This
+* condition also catches running setup_standard_excludes()
+* before setting dir-untracked!
+*/
+   if (dir-unmanaged_exclude_files)
+   return NULL;
+
+   /*
+* Optimize for the main use case only: whole-tree git
+* status. More work involved in treat_leading_path() if we
+* use cache on just a subset of the worktree. pathspec
+* support could make the matter even worse.
+*/
+   if (base_len || (pathspec  pathspec-nr))
+   return NULL;
+
+   /* Different set of flags may produce different results */
+   if (dir-flags != dir-untracked-dir_flags ||
+   /*
+* See treat_directory(), case index_nonexistent. Without
+* this flag, we may need to also cache .git file content
+* for the resolve_gitlink_ref() call, which we don't.
+*/
+   !(dir-flags  DIR_SHOW_OTHER_DIRECTORIES) ||
+   /* We don't support collecting ignore files */
+   (dir-flags  (DIR_SHOW_IGNORED | DIR_SHOW_IGNORED_TOO |
+  DIR_COLLECT_IGNORED)))
+   return NULL;
+
+   /*
+* If we use .gitignore in the cache and now you change it to
+* .gitexclude, everything will go wrong.
+*/
+   if (dir-exclude_per_dir != dir-untracked-exclude_per_dir 
+   strcmp(dir-exclude_per_dir, dir-untracked-exclude_per_dir))
+   return NULL;
+
+   /*
+* EXC_CMDL is not considered in the cache. If people set it,
+* skip the cache.
+*/
+   if (dir-exclude_list_group[EXC_CMDL].nr)
+   return NULL;
+
+   if (!dir-untracked-root) {
+   const int len = sizeof(*dir-untracked-root);
+   dir-untracked-root = xmalloc(len);
+   memset(dir-untracked-root, 0, len);
+   }
+
+   /* Validate $GIT_DIR/info/exclude and core.excludesfile */
+   root = dir-untracked-root;
+   if (hashcmp(dir-ss_info_exclude.sha1,
+   dir-untracked-ss_info_exclude.sha1)) {
+   invalidate_gitignore(dir-untracked, root);

[PATCH 04/24] untracked cache: invalidate dirs recursively if .gitignore changes

2015-02-08 Thread Nguyễn Thái Ngọc Duy
It's easy to see that if an existing .gitignore changes, its SHA-1
would be different and invalidate_gitignore() is called.

If .gitignore is removed, add_excludes() will treat it like an empty
.gitignore, which again should invalidate the cached directory data.

if .gitignore is added, lookup_untracked() already fills initial
.gitignore SHA-1 as empty file, so again invalidate_gitignore() is
called.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
Signed-off-by: Junio C Hamano gits...@pobox.com
---
 dir.c | 18 +-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/dir.c b/dir.c
index 44ed9f2..6e91315 100644
--- a/dir.c
+++ b/dir.c
@@ -1010,7 +1010,23 @@ static void prep_exclude(struct dir_struct *dir, const 
char *base, int baselen)
add_excludes(el-src, el-src, stk-baselen, el, 1,
 untracked ? sha1_stat : NULL);
}
-   if (untracked) {
+   /*
+* NEEDSWORK: when untracked cache is enabled, prep_exclude()
+* will first be called in valid_cached_dir() then maybe many
+* times more in last_exclude_matching(). When the cache is
+* used, last_exclude_matching() will not be called and
+* reading .gitignore content will be a waste.
+*
+* So when it's called by valid_cached_dir() and we can get
+* .gitignore SHA-1 from the index (i.e. .gitignore is not
+* modified on work tree), we could delay reading the
+* .gitignore content until we absolutely need it in
+* last_exclude_matching(). Be careful about ignore rule
+* order, though, if you do that.
+*/
+   if (untracked 
+   hashcmp(sha1_stat.sha1, untracked-exclude_sha1)) {
+   invalidate_gitignore(dir-untracked, untracked);
hashcpy(untracked-exclude_sha1, sha1_stat.sha1);
}
dir-exclude_stack = stk;
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 00/21] nd/list-files updates

2015-02-08 Thread Nguyễn Thái Ngọc Duy
- 02/21: fix a bug that ignores config keys if $LS_COLORS is not defined
- 09/21: use listFiles instead of list-files in config keys
- 17/21: incorporate match_pathspec changes from Junio,
use strbuf_swap in show_as_directory()
- 21/21: use test_decode_color and test_when_finished in t3080

Full diff of changes is

-- 8 --
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 74da715..2dc4788 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -940,7 +940,7 @@ color.status.slot::
to red). The values of these variables may be specified as in
color.branch.slot.
 
-color.list-files::
+color.listFiles::
A boolean to enable/disable color in the output of
linkgit:git-list-files[1]. May be set to `always`, `false` (or
`never`) or `auto` (or `true`), in which case colors are used
@@ -1018,7 +1018,7 @@ column.clean::
Specify the layout when list items in `git clean -i`, which always
shows files and directories in columns. See `column.ui` for details.
 
-column.list-files::
+column.listFiles::
Specify whether to output tag listing in `git list-files` in columns.
See `column.ui` for details.
 
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index b04c712..b5e1a59 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -229,8 +229,8 @@ static int show_as_directory(const struct cache_entry *ce)
if (want_color(use_color)) {
struct strbuf sb3 = STRBUF_INIT;
color_filename(sb3, ce-name, sb2.buf, S_IFDIR, 1);
-   strbuf_release(sb2);
-   sb2 = sb3;
+   strbuf_swap(sb2, sb3);
+   strbuf_release(sb3);
}
if (show_tag)
strbuf_insert(sb2, 0, tag_cached, strlen(tag_cached));
@@ -258,33 +258,41 @@ static void write_ce_name(struct strbuf *sb, const struct 
cache_entry *ce)
strbuf_release(quoted);
 }
 
+static int match_pathspec_with_depth(struct pathspec *ps,
+const char *name, int namelen,
+int prefix, char *seen, int is_dir,
+const int *custom_depth)
+{
+   int saved_depth = ps-max_depth;
+   int result;
+
+   if (custom_depth)
+   ps-max_depth = *custom_depth;
+   result = match_pathspec(ps, name, namelen, prefix, seen, is_dir);
+   if (custom_depth)
+   ps-max_depth = saved_depth;
+   return result;
+}
+
 static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
static struct strbuf sb = STRBUF_INIT;
-   int len = max_prefix_len, saved_max_depth;
+   static const int infinite_depth = -1;
+   int len = max_prefix_len;
 
if (len = ce_namelen(ce))
die(git ls-files: internal error - cache entry not superset of 
prefix);
 
-   if (show_dirs) {
-   /* ignore depth to catch dirs that contain matched entries */
-   saved_max_depth = pathspec.max_depth;
-   pathspec.max_depth = -1;
-   }
-
-   if (!match_pathspec(pathspec, ce-name, ce_namelen(ce),
-   len, ps_matched,
-   S_ISDIR(ce-ce_mode) || S_ISGITLINK(ce-ce_mode)))
+   if (!match_pathspec_with_depth(pathspec, ce-name, ce_namelen(ce),
+  len, ps_matched,
+  S_ISDIR(ce-ce_mode) || 
S_ISGITLINK(ce-ce_mode),
+  show_dirs ? infinite_depth : NULL))
return;
 
-   if (show_dirs) {
-   pathspec.max_depth = saved_max_depth;
-   if (strchr(ce-name, '/') 
-   !match_pathspec(pathspec, ce-name, ce_namelen(ce),
-   prefix_len, NULL, 1) 
-   show_as_directory(ce))
-   return;
-   }
+   if (show_dirs strchr(ce-name, '/') 
+   !match_pathspec(pathspec, ce-name, ce_namelen(ce), prefix_len, 
NULL, 1) 
+   show_as_directory(ce))
+   return;
 
if (tag  *tag  show_valid_bit 
(ce-ce_flags  CE_VALID)) {
@@ -697,8 +705,8 @@ static int option_parse_exclude_standard(const struct 
option *opt,
 static int git_ls_config(const char *var, const char *value, void *cb)
 {
if (starts_with(var, column.))
-   return git_column_config(var, value, list-files, colopts);
-   if (!strcmp(var, color.list-files)) {
+   return git_column_config(var, value, listfiles, colopts);
+   if (!strcmp(var, color.listfiles)) {
use_color = git_config_colorbool(var, value);
return 0;
}
diff --git a/ls_colors.c b/ls_colors.c
index 9259ad3..011a8b9 100644
--- a/ls_colors.c
+++ b/ls_colors.c
@@ -326,8 +326,10 @@ void parse_ls_color(void)

[PATCH/RFD 0/3] worktree.* config keys and submodule and multiple worktrees

2015-02-08 Thread Nguyễn Thái Ngọc Duy
1/3 is a more complete version of a patch I posted earlier [1]. It
makes sure that worktree.* config keys are stored in a different place
than $GIT_DIR/config. This allows us to work around the core.worktree
issue in multiple worktree setting.

I think 1/3 and 2/3 are fine. 3/3 is probably not. It's more of a
proof of concept. The tests pass, but there's no migration path for
existing submodules. Submodules modified by new Git will confuse old
Git because the old ones do not understand worktree.path (the
replacement for core.worktree)

But I think it's a start..

[1] http://article.gmane.org/gmane.comp.version-control.git/263134

Nguyễn Thái Ngọc Duy (3):
  config.c: new config namespace worktree.* stored in $GIT_DIR/config.worktree
  setup: add worktree.path to shadow core.worktree
  submodule: use worktree.path instead of core.worktree

 Documentation/config.txt   |  7 ++-
 builtin/config.c   |  8 +++
 config.c   | 38 ++
 git-submodule.sh   |  2 +-
 setup.c|  7 ++-
 submodule.c|  6 +++---
 t/lib-submodule-update.sh  |  8 +++
 t/t1300-repo-config.sh | 31 +++
 t/t7400-submodule-basic.sh |  4 ++--
 t/t7409-submodule-detached-worktree.sh |  6 +++---
 10 files changed, 102 insertions(+), 15 deletions(-)

-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 3/3] submodule: use worktree.path instead of core.worktree

2015-02-08 Thread Nguyễn Thái Ngọc Duy
This opens a door of using submodule with multiple worktrees

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 git-submodule.sh   | 2 +-
 submodule.c| 6 +++---
 t/lib-submodule-update.sh  | 8 
 t/t7400-submodule-basic.sh | 4 ++--
 t/t7409-submodule-detached-worktree.sh | 6 +++---
 5 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/git-submodule.sh b/git-submodule.sh
index 9245abf..6e9e1d1 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -309,7 +309,7 @@ module_clone()
printf '%s\n' gitdir: $rel/$a $sm_path/.git
 
rel=$(printf '%s\n' $a | sed -e 's|[^/][^/]*|..|g')
-   (clear_local_git_env; cd $sm_path  GIT_WORK_TREE=. git config 
core.worktree $rel/$b)
+   (clear_local_git_env; cd $sm_path  GIT_WORK_TREE=. git config 
worktree.path $rel/$b)
 }
 
 isnumber()
diff --git a/submodule.c b/submodule.c
index d37d400..f886fa6 100644
--- a/submodule.c
+++ b/submodule.c
@@ -1115,11 +1115,11 @@ void connect_work_tree_and_git_dir(const char 
*work_tree, const char *git_dir)
 
/* Update core.worktree setting */
strbuf_reset(file_name);
-   strbuf_addf(file_name, %s/config, git_dir);
-   if (git_config_set_in_file(file_name.buf, core.worktree,
+   strbuf_addf(file_name, %s/config.worktree, git_dir);
+   if (git_config_set_in_file(file_name.buf, worktree.path,
   relative_path(real_work_tree, git_dir,
 rel_path)))
-   die(_(Could not set core.worktree in %s),
+   die(_(Could not set worktree.path in %s),
file_name.buf);
 
strbuf_release(file_name);
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 79cdd34..ce140cf 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -105,7 +105,7 @@ replace_gitfile_with_git_dir () {
git_dir=$(git rev-parse --git-dir) 
rm -f .git 
cp -R $git_dir .git 
-   GIT_WORK_TREE=. git config --unset core.worktree
+   GIT_WORK_TREE=. git config --unset worktree.path
)
 }
 
@@ -120,16 +120,16 @@ test_git_directory_is_unchanged () {
(
cd .git/modules/$1 
# does core.worktree point at the right place?
-   test $(git config core.worktree) = ../../../$1 
+   test $(git config worktree.path) = ../../../$1 
# remove it temporarily before comparing, as
# $1/.git/config lacks it...
-   git config --unset core.worktree
+   git config --unset worktree.path
) 
diff -r .git/modules/$1 $1/.git 
(
# ... and then restore.
cd .git/modules/$1 
-   git config core.worktree ../../../$1
+   git config worktree.path ../../../$1
)
 }
 
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 7c88245..def28e6 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -93,7 +93,7 @@ test_expect_success 'submodule add' '
test_cmp expect submod/.git 
(
cd submod 
-   git config core.worktree actual 
+   git config worktree.path actual 
echo ../../../submod expect 
test_cmp expect actual 
rm -f actual expect
@@ -945,7 +945,7 @@ test_expect_success 'submodule deinit fails when submodule 
has a .git directory
cd init 
rm .git 
cp -R ../.git/modules/example .git 
-   GIT_WORK_TREE=. git config --unset core.worktree
+   GIT_WORK_TREE=. git config --unset worktree.path
) 
test_must_fail git submodule deinit init 
test_must_fail git submodule deinit -f init 
diff --git a/t/t7409-submodule-detached-worktree.sh 
b/t/t7409-submodule-detached-worktree.sh
index c207171..ba50d77 100755
--- a/t/t7409-submodule-detached-worktree.sh
+++ b/t/t7409-submodule-detached-worktree.sh
@@ -55,7 +55,7 @@ test_expect_success 'submodule on detached working tree' '
)
 '
 
-test_expect_success 'submodule on detached working pointed by core.worktree' '
+test_expect_success 'submodule on detached working pointed by worktree.path' '
mkdir home3 
(
cd home3 
@@ -63,7 +63,7 @@ test_expect_success 'submodule on detached working pointed by 
core.worktree' '
export GIT_DIR 
git clone --bare ../remote $GIT_DIR 
git config core.bare false 
-   git config core.worktree .. 
+   git config worktree.path .. 
git checkout master 
git submodule add ../bundle1 .vim/bundle/dupe 
test_commit dupe 
@@ 

[PATCH 2/3] setup: add worktree.path to shadow core.worktree

2015-02-08 Thread Nguyễn Thái Ngọc Duy
They have the same purpose. But they are located in different places:
core.worktree in $GIT_DIR/config while worktree.path in
$GIT_DIR/config.worktree

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/config.txt | 3 +++
 setup.c  | 7 ++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 26e4e07..b717881 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -410,6 +410,7 @@ false), while all other repositories are assumed to be bare 
(bare
 = true).
 
 core.worktree::
+worktree.path::
Set the path to the root of the working tree.
This can be overridden by the GIT_WORK_TREE environment
variable and the '--work-tree' command-line option.
@@ -430,6 +431,8 @@ still use /different/path as the root of the work tree 
and can cause
 confusion unless you know what you are doing (e.g. you are creating a
 read-only snapshot of the same index to a location different from the
 repository's usual working tree).
++
+worktree.path takes precedence over core.worktree.
 
 core.logAllRefUpdates::
Enable the reflog. Updates to a ref ref is logged to the file
diff --git a/setup.c b/setup.c
index 979b13f..bc27f8b 100644
--- a/setup.c
+++ b/setup.c
@@ -4,6 +4,7 @@
 
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
+static int worktree_path_set;
 
 /*
  * The input parameter must contain an absolute path, and it must already be
@@ -807,12 +808,16 @@ int check_repository_format_version(const char *var, 
const char *value, void *cb
is_bare_repository_cfg = git_config_bool(var, value);
if (is_bare_repository_cfg == 1)
inside_work_tree = -1;
-   } else if (strcmp(var, core.worktree) == 0) {
+   } else if (strcmp(var, worktree.path) == 0 ||
+  (strcmp(var, core.worktree) == 0 
+   !worktree_path_set)) {
if (!value)
return config_error_nonbool(var);
free(git_work_tree_cfg);
git_work_tree_cfg = xstrdup(value);
inside_work_tree = -1;
+   if (!strcmp(var, worktree.path))
+   worktree_path_set = 1;
}
return 0;
 }
-- 
2.3.0.rc1.137.g477eb31

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 1/3] config.c: new config namespace worktree.* stored in $GIT_DIR/config.worktree

2015-02-08 Thread Nguyễn Thái Ngọc Duy
On the surface, worktree.* variables are just like any other
variables. You can read or modify them with `git config` command, or
config_* API. Underneath, worktree.* are always stored in
$GIT_DIR/config.worktree instead of $GIT_DIR/config (and never in
$HOME/.gitconfig or /etc/gitconfig)

The reason for this split is, as the name implies, for
worktree-specific configuration. When the same repo is attached to
more than one worktree, we can't use $GIT_DIR/config to store worktree
specific stuff because it's shared across worktrees.

With this, both git-new-workdir and nd/multiple-work-trees will work.
git-new-workdir creates a symlink to $GIT_DIR/config in the new
worktree, but not $GIT_DIR/config.worktree. That file will be created
new when worktree.* are written in the new worktree.
`git checkout --to` can remap the path $GIT_DIR/config.worktree to make
it worktree-specific.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/config.txt |  4 +++-
 builtin/config.c |  8 
 config.c | 38 ++
 t/t1300-repo-config.sh   | 31 +++
 4 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 04e2a71..26e4e07 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -2,11 +2,13 @@ CONFIGURATION FILE
 --
 
 The Git configuration file contains a number of variables that affect
-the Git commands' behavior. The `.git/config` file in each repository
+the Git commands' behavior. The `.git/config` and `.git/config.worktree`
+files in each repository
 is used to store the configuration for that repository, and
 `$HOME/.gitconfig` is used to store a per-user configuration as
 fallback values for the `.git/config` file. The file `/etc/gitconfig`
 can be used to store a system-wide default configuration.
+worktree.* variables can only be contained in `.git/config.worktree`
 
 The configuration variables are used by both the Git plumbing
 and the porcelains. The variables are divided into sections, wherein
diff --git a/builtin/config.c b/builtin/config.c
index 15a7bea..d9333cd 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -552,6 +552,14 @@ int cmd_config(int argc, const char **argv, const char 
*prefix)
usage_with_options(builtin_config_usage, 
builtin_config_options);
}
 
+   /*
+* For set operations, --local could be either config or
+* config.worktree. Let config.c determine the path based on
+* config keys.
+*/
+   if (use_local_config  actions != ACTION_LIST)
+   given_config_source.file = NULL;
+
if (actions == ACTION_LIST) {
check_argc(argc, 0, 0);
if (git_config_with_options(show_all_config, NULL,
diff --git a/config.c b/config.c
index 752e2e2..3d46192 100644
--- a/config.c
+++ b/config.c
@@ -12,6 +12,7 @@
 #include quote.h
 #include hashmap.h
 #include string-list.h
+#include dir.h
 
 struct config_source {
struct config_source *prev;
@@ -1177,6 +1178,15 @@ int git_config_system(void)
return !git_env_bool(GIT_CONFIG_NOSYSTEM, 0);
 }
 
+static int config_worktree_filter(const char *var, const char *value, void 
*data)
+{
+   struct config_include_data *inc = data;
+
+   if (!starts_with(var, worktree.))
+   return error($GIT_DIR/config.worktree can only contain 
worktree.*);
+   return inc-fn(var, value, inc-data);
+}
+
 int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
int ret = 0, found = 0;
@@ -1202,8 +1212,19 @@ int git_config_early(config_fn_t fn, void *data, const 
char *repo_config)
}
 
if (repo_config  !access_or_die(repo_config, R_OK, 0)) {
+   char *wt_config;
ret += git_config_from_file(fn, repo_config, data);
found += 1;
+   wt_config = git_pathdup(config.worktree);
+   if (!access_or_die(wt_config, R_OK, 0)) {
+   struct config_include_data inc = CONFIG_INCLUDE_INIT;
+   inc.fn = fn;
+   inc.data = data;
+   ret += git_config_from_file(config_worktree_filter,
+   wt_config, inc);
+   found += 1;
+   }
+   free(wt_config);
}
 
switch (git_config_from_parameters(fn, data)) {
@@ -1942,6 +1963,23 @@ int git_config_set_multivar_in_file(const char 
*config_filename,
 
store.multi_replace = multi_replace;
 
+   if (starts_with(key, worktree.)) {
+   if (!config_filename)
+   config_filename = filename_buf = 
git_pathdup(config.worktree);
+   /* cheap trick, but should work 90% of time */
+   else if (!ends_with(config_filename, .worktree))
+  

Re: 'git rebase' silently drops changes?

2015-02-08 Thread Johannes Sixt
Am 07.02.2015 um 22:32 schrieb Sebastian Schuberth:
 On 06.02.2015 22:28, Sergey Organov wrote:
 
 # Now rebase my work.
 git rebase -f HEAD~1

 # What? Where is my Precious change in a???
 cat a
 /SCRIPT

 I.e., the modification marked [!] was silently lost during rebase!
 
 Just a wild guess: Maybe because you omitted -p / --preserve-merges
 from git rebase?

No, that would not help. --preserve-merges repeats the merge, but does
not apply the amendment.

It's just how rebase works: It omits merge commits when it linearizes
history.

Sergey, it is impossible for git rebase to decide to which rebased
commit the amendement applies. It doesn't even try to guess. It's the
responsibility of the user to apply the amendment to the correct commit.

-- Hannes

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: read() MAX_IO_SIZE bytes, more than SSIZE_MAX?

2015-02-08 Thread Joachim Schmitz
Junio C Hamano gitster at pobox.com writes:

snip
 
  something like this:
 
  /* allow overwriting from e.g. Makefile */
  #if !defined(MAX_IO_SIZE)
  # define MAX_IO_SIZE (8*1024*1024)
  #endif
  /* for plattforms that have SSIZE and have it smaller */
  #if defined(SSIZE_MAX  (SSIZE_MAX  MAX_IO_SIZE)
  # undef MAX_IO_SIZE /* avoid warning */
  # define MAX_IO_SIZE SSIZE_MAX
  #endif
 
 No, not like that. If you do (1), that is only so that the Makefile can 
override
 a broken definition a platform may give to SSIZE_MAX.  So
 
  (1) if Makefile gives one, use it without second-guessing with SSIZE_MAX.
  (2) if SSIZE_MAX is defined, and if it is smaller than our internal
 default, use it.
  (3) all other cases, us our internal default.


oops, yes, of course

/* allow overwriting from e.g. Makefile */
#ifndef(MAX_IO_SIZE)
# define MAX_IO_SIZE (8*1024*1024)
  /* for plattforms that have SSIZE and have it smaller */
# if defined(SSIZE_MAX)  (SSIZE_MAX  MAX_IO_SIZE)
#  undef MAX_IO_SIZE /* avoid warning */
#  define MAX_IO_SIZE SSIZE_MAX
# endif
#endif







--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFD 0/3] worktree.* config keys and submodule and multiple worktrees

2015-02-08 Thread Jens Lehmann

Am 08.02.2015 um 09:36 schrieb Jens Lehmann:

Am 08.02.2015 um 05:16 schrieb Nguyễn Thái Ngọc Duy:

1/3 is a more complete version of a patch I posted earlier [1]. It
makes sure that worktree.* config keys are stored in a different place
than $GIT_DIR/config. This allows us to work around the core.worktree
issue in multiple worktree setting.

I think 1/3 and 2/3 are fine. 3/3 is probably not. It's more of a
proof of concept. The tests pass, but there's no migration path for
existing submodules. Submodules modified by new Git will confuse old
Git because the old ones do not understand worktree.path (the
replacement for core.worktree)


Yeah, breaking every submodule cloned with v1.7.9 or newer is not


That's v1.7.8 of course ...


the way to go. You'd still have to support the old configuration
name for a very long time.

I wonder if it's worth all the hassle to invent new names. Wouldn't
it be much better to just keep a list of per-worktree configuration
value names and use that inside the config code to decide where to
find them for multiple work trees. That would also work easily for
stuff like EOL-config and would push the complexity in the config
machinery and not onto the user.


--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Fwd: segmentation fault 11

2015-02-08 Thread David Krmpotic
I updated to 2.3.0 on my OSX 10.9.5 and when trying to push to github
(git push -u origin master), I get:

Segmentation fault: 11

Switched back to 1.8.5.5 and it works...

Report: http://cl.ly/1u3E412N0D2Z

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x

VM Regions Near 0:
--
__TEXT 000102b1c000-000102c78000 [ 1392K]
r-x/rwx SM=COW  /usr/local/Cellar/git/2.3.0/bin/git

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 10/11] ref_transaction_verify(): new function to check a reference's value

2015-02-08 Thread Michael Haggerty
If NULL is passed to ref_transaction_update()'s new_sha1 parameter,
then just verify old_sha1 (under lock) without trying to change the
new value of the reference.

Use this functionality to add a new function ref_transaction_verify(),
which checks the current value of the reference under lock but doesn't
change it.

Use ref_transaction_verify() in the implementation of git update-ref
--stdin's verify command to avoid the awkward need to update the
reference to its existing value.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 builtin/update-ref.c |  7 ++-
 refs.c   | 47 +++
 refs.h   | 34 ++
 3 files changed, 67 insertions(+), 21 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 76b8aef..a2eedba 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -282,7 +282,6 @@ static const char *parse_cmd_verify(struct ref_transaction 
*transaction,
 {
struct strbuf err = STRBUF_INIT;
char *refname;
-   unsigned char new_sha1[20];
unsigned char old_sha1[20];
 
refname = parse_refname(input, next);
@@ -293,13 +292,11 @@ static const char *parse_cmd_verify(struct 
ref_transaction *transaction,
PARSE_SHA1_OLD))
hashclr(old_sha1);
 
-   hashcpy(new_sha1, old_sha1);
-
if (*next != line_termination)
die(verify %s: extra input: %s, refname, next);
 
-   if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
-  update_flags, msg, err))
+   if (ref_transaction_verify(transaction, refname, old_sha1,
+  update_flags, err))
die(%s, err.buf);
 
update_flags = 0;
diff --git a/refs.c b/refs.c
index 85815d7..0da680e 100644
--- a/refs.c
+++ b/refs.c
@@ -47,10 +47,16 @@ static unsigned char refname_disposition[256] = {
 #define REF_ISPRUNING  0x04
 
 /*
+ * Used as a flag in ref_update::flags when the reference should be
+ * updated to new_sha1.
+ */
+#define REF_HAVE_NEW   0x08
+
+/*
  * Used as a flag in ref_update::flags when old_sha1 should be
  * checked.
  */
-#define REF_HAVE_OLD   0x08
+#define REF_HAVE_OLD   0x10
 
 /*
  * Try to read one refname component from the front of refname.
@@ -3578,10 +3584,17 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
  * not exist before update.
  */
 struct ref_update {
+   /*
+* If (flags  REF_HAVE_NEW), set the reference to this value:
+*/
unsigned char new_sha1[20];
+   /*
+* If (flags  REF_HAVE_OLD), check that the reference
+* previously had this value:
+*/
unsigned char old_sha1[20];
/*
-* One or more of REF_HAVE_OLD, REF_NODEREF,
+* One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
 * REF_DELETING, and REF_IS_PRUNING:
 */
int flags;
@@ -3666,7 +3679,7 @@ int ref_transaction_update(struct ref_transaction 
*transaction,
if (transaction-state != REF_TRANSACTION_OPEN)
die(BUG: update called for transaction that is not open);
 
-   if (!is_null_sha1(new_sha1) 
+   if (new_sha1  !is_null_sha1(new_sha1) 
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
strbuf_addf(err, refusing to update ref with bad name %s,
refname);
@@ -3674,7 +3687,10 @@ int ref_transaction_update(struct ref_transaction 
*transaction,
}
 
update = add_update(transaction, refname);
-   hashcpy(update-new_sha1, new_sha1);
+   if (new_sha1) {
+   hashcpy(update-new_sha1, new_sha1);
+   flags |= REF_HAVE_NEW;
+   }
if (old_sha1) {
hashcpy(update-old_sha1, old_sha1);
flags |= REF_HAVE_OLD;
@@ -3710,6 +3726,19 @@ int ref_transaction_delete(struct ref_transaction 
*transaction,
  flags, msg, err);
 }
 
+int ref_transaction_verify(struct ref_transaction *transaction,
+  const char *refname,
+  const unsigned char *old_sha1,
+  int flags,
+  struct strbuf *err)
+{
+   if (!old_sha1)
+   die(BUG: verify called with old_sha1 set to NULL);
+   return ref_transaction_update(transaction, refname,
+ NULL, old_sha1,
+ flags, NULL, err);
+}
+
 int update_ref(const char *action, const char *refname,
   const unsigned char *sha1, const unsigned char *oldval,
   int flags, enum action_on_err onerr)
@@ -3798,7 +3827,7 @@ int ref_transaction_commit(struct ref_transaction 
*transaction,
struct ref_update *update = updates[i];
int flags = update-flags;
 
-   if 

[PATCH 01/11] refs: move REF_DELETING to refs.c

2015-02-08 Thread Michael Haggerty
It is only used internally now. Document it a little bit better, too.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 refs.c | 6 ++
 refs.h | 4 +---
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/refs.c b/refs.c
index c5fa709..cd5208b 100644
--- a/refs.c
+++ b/refs.c
@@ -35,6 +35,12 @@ static unsigned char refname_disposition[256] = {
 };
 
 /*
+ * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken
+ * refs (i.e., because the reference is about to be deleted anyway).
+ */
+#define REF_DELETING   0x02
+
+/*
  * Used as a flag to ref_transaction_delete when a loose ref is being
  * pruned.
  */
diff --git a/refs.h b/refs.h
index afa3c4d..9bf2148 100644
--- a/refs.h
+++ b/refs.h
@@ -183,12 +183,10 @@ extern int peel_ref(const char *refname, unsigned char 
*sha1);
  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.
  * REF_NODEREF: act on the ref directly, instead of dereferencing
  *  symbolic references.
- * REF_DELETING: tolerate broken refs
  *
- * Flags = 0x100 are reserved for internal use.
+ * Other flags are reserved for internal use.
  */
 #define REF_NODEREF0x01
-#define REF_DELETING   0x02
 
 /*
  * Setup reflog before using. Set errno to something meaningful on failure.
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 09/11] ref_transaction_delete(): check that old_sha1 is not null_sha1

2015-02-08 Thread Michael Haggerty
It makes no sense to delete a reference that is already known not to
exist.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 refs.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/refs.c b/refs.c
index 8ab1f8e..85815d7 100644
--- a/refs.c
+++ b/refs.c
@@ -3703,6 +3703,8 @@ int ref_transaction_delete(struct ref_transaction 
*transaction,
   int flags, const char *msg,
   struct strbuf *err)
 {
+   if (old_sha1  is_null_sha1(old_sha1))
+   die(BUG: delete called with old_sha1 set to zeros);
return ref_transaction_update(transaction, refname,
  null_sha1, old_sha1,
  flags, msg, err);
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 08/11] ref_transaction_create(): check that new_sha1 is valid

2015-02-08 Thread Michael Haggerty
Creating a reference requires a new_sha1 that is not NULL and not
null_sha1.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 refs.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/refs.c b/refs.c
index 6bd6581..8ab1f8e 100644
--- a/refs.c
+++ b/refs.c
@@ -3691,6 +3691,8 @@ int ref_transaction_create(struct ref_transaction 
*transaction,
   int flags, const char *msg,
   struct strbuf *err)
 {
+   if (!new_sha1 || is_null_sha1(new_sha1))
+   die(BUG: create called without valid new_sha1);
return ref_transaction_update(transaction, refname, new_sha1,
  null_sha1, flags, msg, err);
 }
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 06/11] commit: add tests of commit races

2015-02-08 Thread Michael Haggerty
Committing involves the following steps:

1. Determine the current value of HEAD (if any).
2. Create the new commit object.
3. Update HEAD.

Please note that step 2 can take arbitrarily long, because it might
involve the user editing a commit message.

If a second process sneaks in a commit during step 2, then the first
commit process should fail. This is usually done correctly, because
step 3 verifies that HEAD still points at the same commit that it
pointed to during step 1.

However, if there is a race when creating an *orphan* commit, then the
test in step 3 is skipped.

Add tests for proper handling of such races. One of the new tests
fails. It will be fixed in a moment.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 t/t7516-commit-races.sh | 38 ++
 1 file changed, 38 insertions(+)
 create mode 100755 t/t7516-commit-races.sh

diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh
new file mode 100755
index 000..5efa351
--- /dev/null
+++ b/t/t7516-commit-races.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Michael Haggerty mhag...@alum.mit.edu
+#
+
+test_description='git commit races'
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success 'set up editor' '
+   cat editor -\EOF 
+   #!/bin/sh
+   git commit --allow-empty -m hare
+   echo tortoise $1
+   EOF
+   chmod +x editor
+'
+
+test_expect_failure 'race to create orphan commit' '
+   test_must_fail env EDITOR=./editor git commit --allow-empty 
+   git show -s --pretty=format:%s subject 
+   grep -q hare subject 
+   test -z $(git show -s --pretty=format:%P)
+'
+
+test_expect_success 'race to create non-orphan commit' '
+   git checkout --orphan branch 
+   git commit --allow-empty -m base 
+   git rev-parse HEAD base 
+   test_must_fail env EDITOR=./editor git commit --allow-empty 
+   git show -s --pretty=format:%s subject 
+   grep -q hare subject 
+   git rev-parse HEAD^ parent 
+   test_cmp base parent
+'
+
+test_done
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 05/11] ref_transaction_delete(): remove have_old parameter

2015-02-08 Thread Michael Haggerty
Instead, if old_sha1 is non-NULL, verify it; otherwise, don't.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 builtin/receive-pack.c |  3 +--
 builtin/update-ref.c   |  5 +++--
 refs.c | 11 ++-
 refs.h |  6 +++---
 4 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 71be82e..b08806d 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -933,8 +933,7 @@ static const char *update(struct command *cmd, struct 
shallow_info *si)
if (ref_transaction_delete(transaction,
   namespaced_name,
   old_sha1,
-  0, old_sha1 != NULL,
-  push, err)) {
+  0, push, err)) {
rp_error(%s, err.buf);
strbuf_release(err);
return failed to delete;
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index f0db7a3..76b8aef 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -265,8 +265,9 @@ static const char *parse_cmd_delete(struct ref_transaction 
*transaction,
if (*next != line_termination)
die(delete %s: extra input: %s, refname, next);
 
-   if (ref_transaction_delete(transaction, refname, old_sha1,
-  update_flags, have_old, msg, err))
+   if (ref_transaction_delete(transaction, refname,
+  have_old ? old_sha1 : NULL,
+  update_flags, msg, err))
die(%s, err.buf);
 
update_flags = 0;
diff --git a/refs.c b/refs.c
index 6beafcf..6bd6581 100644
--- a/refs.c
+++ b/refs.c
@@ -2577,7 +2577,7 @@ static void prune_ref(struct ref_to_prune *r)
transaction = ref_transaction_begin(err);
if (!transaction ||
ref_transaction_delete(transaction, r-name, r-sha1,
-  REF_ISPRUNING, 1, NULL, err) ||
+  REF_ISPRUNING, NULL, err) ||
ref_transaction_commit(transaction, err)) {
ref_transaction_free(transaction);
error(%s, err.buf);
@@ -2754,8 +2754,9 @@ int delete_ref(const char *refname, const unsigned char 
*sha1, int delopt)
 
transaction = ref_transaction_begin(err);
if (!transaction ||
-   ref_transaction_delete(transaction, refname, sha1, delopt,
-  sha1  !is_null_sha1(sha1), NULL, err) ||
+   ref_transaction_delete(transaction, refname,
+  (sha1  !is_null_sha1(sha1)) ? sha1 : NULL,
+  delopt, NULL, err) ||
ref_transaction_commit(transaction, err)) {
error(%s, err.buf);
ref_transaction_free(transaction);
@@ -3697,11 +3698,11 @@ int ref_transaction_create(struct ref_transaction 
*transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
   const char *refname,
   const unsigned char *old_sha1,
-  int flags, int have_old, const char *msg,
+  int flags, const char *msg,
   struct strbuf *err)
 {
return ref_transaction_update(transaction, refname,
- null_sha1, have_old ? old_sha1 : NULL,
+ null_sha1, old_sha1,
  flags, msg, err);
 }
 
diff --git a/refs.h b/refs.h
index f05ac89..ee612da 100644
--- a/refs.h
+++ b/refs.h
@@ -295,8 +295,8 @@ int ref_transaction_create(struct ref_transaction 
*transaction,
   struct strbuf *err);
 
 /*
- * Add a reference deletion to transaction.  If have_old is true, then
- * old_sha1 holds the value that the reference should have had before
+ * Add a reference deletion to transaction.  If old_sha1 is non-NULL, then
+ * it holds the value that the reference should have had before
  * the update (which must not be the null SHA-1).
  * Function returns 0 on success and non-zero on failure. A failure to delete
  * means that the transaction as a whole has failed and will need to be
@@ -305,7 +305,7 @@ int ref_transaction_create(struct ref_transaction 
*transaction,
 int ref_transaction_delete(struct ref_transaction *transaction,
   const char *refname,
   const unsigned char *old_sha1,
-  int flags, int have_old, const char *msg,
+  int flags, const char *msg,
   struct strbuf *err);
 
 /*
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  

[PATCH 04/11] ref_transaction_update(): remove have_old parameter

2015-02-08 Thread Michael Haggerty
Instead, if old_sha1 is non-NULL, verify it; otherwise, don't.

ref_transaction_delete() will get the same treatment in a moment.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 branch.c   |  5 +++--
 builtin/commit.c   |  2 +-
 builtin/fetch.c|  6 --
 builtin/receive-pack.c |  2 +-
 builtin/replace.c  |  2 +-
 builtin/tag.c  |  2 +-
 builtin/update-ref.c   |  7 ---
 fast-import.c  |  6 +++---
 refs.c | 18 --
 refs.h |  6 +++---
 sequencer.c|  2 +-
 walker.c   |  2 +-
 12 files changed, 31 insertions(+), 29 deletions(-)

diff --git a/branch.c b/branch.c
index 4bab55a..b002435 100644
--- a/branch.c
+++ b/branch.c
@@ -284,8 +284,9 @@ void create_branch(const char *head,
 
transaction = ref_transaction_begin(err);
if (!transaction ||
-   ref_transaction_update(transaction, ref.buf, sha1,
-  null_sha1, 0, !forcing, msg, err) ||
+   ref_transaction_update(transaction, ref.buf,
+  sha1, forcing ? NULL : null_sha1,
+  0, msg, err) ||
ref_transaction_commit(transaction, err))
die(%s, err.buf);
ref_transaction_free(transaction);
diff --git a/builtin/commit.c b/builtin/commit.c
index 7d90c35..5654abd 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1773,7 +1773,7 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
ref_transaction_update(transaction, HEAD, sha1,
   current_head
   ? current_head-object.sha1 : NULL,
-  0, !!current_head, sb.buf, err) ||
+  0, sb.buf, err) ||
ref_transaction_commit(transaction, err)) {
rollback_index_files();
die(%s, err.buf);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 7b84d35..719bf4f 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -416,8 +416,10 @@ static int s_update_ref(const char *action,
 
transaction = ref_transaction_begin(err);
if (!transaction ||
-   ref_transaction_update(transaction, ref-name, ref-new_sha1,
-  ref-old_sha1, 0, check_old, msg, err))
+   ref_transaction_update(transaction, ref-name,
+  ref-new_sha1,
+  check_old ? ref-old_sha1 : NULL,
+  0, msg, err))
goto fail;
 
ret = ref_transaction_commit(transaction, err);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 4e85e25..71be82e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -951,7 +951,7 @@ static const char *update(struct command *cmd, struct 
shallow_info *si)
if (ref_transaction_update(transaction,
   namespaced_name,
   new_sha1, old_sha1,
-  0, 1, push,
+  0, push,
   err)) {
rp_error(%s, err.buf);
strbuf_release(err);
diff --git a/builtin/replace.c b/builtin/replace.c
index 85d39b5..54bf01a 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -172,7 +172,7 @@ static int replace_object_sha1(const char *object_ref,
transaction = ref_transaction_begin(err);
if (!transaction ||
ref_transaction_update(transaction, ref, repl, prev,
-  0, 1, NULL, err) ||
+  0, NULL, err) ||
ref_transaction_commit(transaction, err))
die(%s, err.buf);
 
diff --git a/builtin/tag.c b/builtin/tag.c
index e633f4e..232c28c 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -733,7 +733,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
transaction = ref_transaction_begin(err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf, object, prev,
-  0, 1, NULL, err) ||
+  0, NULL, err) ||
ref_transaction_commit(transaction, err))
die(%s, err.buf);
ref_transaction_free(transaction);
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 1993529..f0db7a3 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -198,8 +198,9 @@ static const char *parse_cmd_update(struct ref_transaction 
*transaction,
if (*next != line_termination)
die(update %s: extra input: %s, refname, next);
 
-   if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
- 

[PATCH 00/11] Allow reference values to be checked in a transaction

2015-02-08 Thread Michael Haggerty
The main purpose of this series is to simplify the interface to
reference transactions as follows:

* Remove the need to supply an explicit have_old parameter to
  ref_transaction_update() and ref_transaction_delete(). Instead,
  check the old_sha1 if and only if it is non-NULL.

* Allow NULL to be supplied to ref_transaction_update() as new_sha1,
  in which case old_sha1 will be verified under lock, but the
  reference's value will not be altered.

* Add a function ref_transaction_verify(), which verifies the current
  value of a reference without changing it.

* Make the similarity between ref_transaction_update() and
  update_ref() more obvious.

Along the way, it fixes a race that could happen if two processes try
to create an orphan commit at the same time.

This patch series applies on top of master merged together with
sb/atomic-push, which in turn depends on mh/reflog-expire. It is also
available from my GitHub account [1] as branch refs-have-new:

It's nothing earth-shattering, but I think it is a worthwhile cleanup.

Michael

[1] https://github.com/mhagger/git

Michael Haggerty (11):
  refs: move REF_DELETING to refs.c
  refs: remove the gap in the REF_* constant values
  struct ref_update: move have_old into flags
  ref_transaction_update(): remove have_old parameter
  ref_transaction_delete(): remove have_old parameter
  commit: add tests of commit races
  commit: avoid race when creating orphan commits
  ref_transaction_create(): check that new_sha1 is valid
  ref_transaction_delete(): check that old_sha1 is not null_sha1
  ref_transaction_verify(): new function to check a reference's value
  update_ref(): improve documentation

 branch.c|   5 +-
 builtin/commit.c|   4 +-
 builtin/fetch.c |   6 ++-
 builtin/receive-pack.c  |   5 +-
 builtin/replace.c   |   2 +-
 builtin/tag.c   |   2 +-
 builtin/update-ref.c|  17 +++
 fast-import.c   |   6 +--
 refs.c  | 130 +---
 refs.h  |  61 ---
 sequencer.c |   2 +-
 t/t7516-commit-races.sh |  38 ++
 walker.c|   2 +-
 13 files changed, 197 insertions(+), 83 deletions(-)
 create mode 100755 t/t7516-commit-races.sh

-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 02/11] refs: remove the gap in the REF_* constant values

2015-02-08 Thread Michael Haggerty
There is no reason to reserve a gap between the public and private
flags values.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 refs.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/refs.c b/refs.c
index cd5208b..477a5b3 100644
--- a/refs.c
+++ b/refs.c
@@ -44,7 +44,8 @@ static unsigned char refname_disposition[256] = {
  * Used as a flag to ref_transaction_delete when a loose ref is being
  * pruned.
  */
-#define REF_ISPRUNING  0x0100
+#define REF_ISPRUNING  0x04
+
 /*
  * Try to read one refname component from the front of refname.
  * Return the length of the component found, or -1 if the component is
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 11/11] update_ref(): improve documentation

2015-02-08 Thread Michael Haggerty
Add a docstring for update_ref(), emphasizing its similarity to
ref_transaction_update(). Rename its parameters to match those of
ref_transaction_update().

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 refs.c |  8 
 refs.h | 15 +++
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/refs.c b/refs.c
index 0da680e..2651583 100644
--- a/refs.c
+++ b/refs.c
@@ -3739,8 +3739,8 @@ int ref_transaction_verify(struct ref_transaction 
*transaction,
  flags, NULL, err);
 }
 
-int update_ref(const char *action, const char *refname,
-  const unsigned char *sha1, const unsigned char *oldval,
+int update_ref(const char *msg, const char *refname,
+  const unsigned char *new_sha1, const unsigned char *old_sha1,
   int flags, enum action_on_err onerr)
 {
struct ref_transaction *t;
@@ -3748,8 +3748,8 @@ int update_ref(const char *action, const char *refname,
 
t = ref_transaction_begin(err);
if (!t ||
-   ref_transaction_update(t, refname, sha1, oldval,
-  flags, action, err) ||
+   ref_transaction_update(t, refname, new_sha1, old_sha1,
+  flags, msg, err) ||
ref_transaction_commit(t, err)) {
const char *str = update_ref failed for ref '%s': %s;
 
diff --git a/refs.h b/refs.h
index 02d6e39..08abd2c 100644
--- a/refs.h
+++ b/refs.h
@@ -344,10 +344,17 @@ int ref_transaction_commit(struct ref_transaction 
*transaction,
  */
 void ref_transaction_free(struct ref_transaction *transaction);
 
-/** Lock a ref and then write its file */
-int update_ref(const char *action, const char *refname,
-   const unsigned char *sha1, const unsigned char *oldval,
-   int flags, enum action_on_err onerr);
+/**
+ * Lock, update, and unlock a single reference. This function
+ * basically does a transaction containing a single call to
+ * ref_transaction_update(). The parameters to this function have the
+ * same meaning as the corresponding parameters to
+ * ref_transaction_update(). Handle errors as requested by the `onerr`
+ * argument.
+ */
+int update_ref(const char *msg, const char *refname,
+  const unsigned char *new_sha1, const unsigned char *old_sha1,
+  int flags, enum action_on_err onerr);
 
 extern int parse_hide_refs_config(const char *var, const char *value, const 
char *);
 extern int ref_is_hidden(const char *);
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 03/11] struct ref_update: move have_old into flags

2015-02-08 Thread Michael Haggerty
Instead of having a separate have_old field, record this boolean value
as a bit in the flags field.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 refs.c | 45 -
 1 file changed, 28 insertions(+), 17 deletions(-)

diff --git a/refs.c b/refs.c
index 477a5b3..851c9f8 100644
--- a/refs.c
+++ b/refs.c
@@ -41,12 +41,18 @@ static unsigned char refname_disposition[256] = {
 #define REF_DELETING   0x02
 
 /*
- * Used as a flag to ref_transaction_delete when a loose ref is being
+ * Used as a flag in ref_update::flags when a loose ref is being
  * pruned.
  */
 #define REF_ISPRUNING  0x04
 
 /*
+ * Used as a flag in ref_update::flags when old_sha1 should be
+ * checked.
+ */
+#define REF_HAVE_OLD   0x08
+
+/*
  * Try to read one refname component from the front of refname.
  * Return the length of the component found, or -1 if the component is
  * not legal.  It is legal if it is something reasonable to have under
@@ -3564,16 +3570,20 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
 }
 
 /**
- * Information needed for a single ref update.  Set new_sha1 to the
- * new value or to zero to delete the ref.  To check the old value
- * while locking the ref, set have_old to 1 and set old_sha1 to the
- * value or to zero to ensure the ref does not exist before update.
+ * Information needed for a single ref update. Set new_sha1 to the new
+ * value or to null_sha1 to delete the ref. To check the old value
+ * while the ref is locked, set (flags  REF_HAVE_OLD) and set
+ * old_sha1 to the old value, or to null_sha1 to ensure the ref does
+ * not exist before update.
  */
 struct ref_update {
unsigned char new_sha1[20];
unsigned char old_sha1[20];
-   int flags; /* REF_NODEREF? */
-   int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
+   /*
+* One or more of REF_HAVE_OLD, REF_NODEREF,
+* REF_DELETING, and REF_IS_PRUNING:
+*/
+   int flags;
struct ref_lock *lock;
int type;
char *msg;
@@ -3667,10 +3677,11 @@ int ref_transaction_update(struct ref_transaction 
*transaction,
 
update = add_update(transaction, refname);
hashcpy(update-new_sha1, new_sha1);
-   update-flags = flags;
-   update-have_old = have_old;
-   if (have_old)
+   if (have_old) {
hashcpy(update-old_sha1, old_sha1);
+   flags |= REF_HAVE_OLD;
+   }
+   update-flags = flags;
if (msg)
update-msg = xstrdup(msg);
return 0;
@@ -3786,13 +3797,13 @@ int ref_transaction_commit(struct ref_transaction 
*transaction,
 
if (is_null_sha1(update-new_sha1))
flags |= REF_DELETING;
-   update-lock = lock_ref_sha1_basic(update-refname,
-  (update-have_old ?
-   update-old_sha1 :
-   NULL),
-  NULL,
-  flags,
-  update-type);
+   update-lock = lock_ref_sha1_basic(
+   update-refname,
+   ((update-flags  REF_HAVE_OLD) ?
+update-old_sha1 : NULL),
+   NULL,
+   flags,
+   update-type);
if (!update-lock) {
ret = (errno == ENOTDIR)
? TRANSACTION_NAME_CONFLICT
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: read() MAX_IO_SIZE bytes, more than SSIZE_MAX?

2015-02-08 Thread Eric Sunshine
On Sun, Feb 8, 2015 at 7:05 AM, Joachim Schmitz j...@schmitz-digital.de wrote:
 Junio C Hamano gitster at pobox.com writes:
  (1) if Makefile gives one, use it without second-guessing with SSIZE_MAX.
  (2) if SSIZE_MAX is defined, and if it is smaller than our internal
 default, use it.
  (3) all other cases, us our internal default.

 oops, yes, of course

 /* allow overwriting from e.g. Makefile */
 #ifndef(MAX_IO_SIZE)
 # define MAX_IO_SIZE (8*1024*1024)
   /* for plattforms that have SSIZE and have it smaller */
 # if defined(SSIZE_MAX)  (SSIZE_MAX  MAX_IO_SIZE)
 #  undef MAX_IO_SIZE /* avoid warning */
 #  define MAX_IO_SIZE SSIZE_MAX
 # endif
 #endif

A bit cleaner:

#ifndef(MAX_IO_SIZE)
# define MAX_IO_SIZE_DEFAULT (8*1024*1024)
# if defined(SSIZE_MAX)  (SSIZE_MAX  MAX_IO_SIZE_DEFAULT)
#  define MAX_IO_SIZE SSIZE_MAX
# else
#  define MAX_IO_SIZE MAX_IO_SIZE_DEFAULT
# endif
#endif
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH/RFD 0/3] worktree.* config keys and submodule and multiple worktrees

2015-02-08 Thread Jens Lehmann

Am 08.02.2015 um 05:16 schrieb Nguyễn Thái Ngọc Duy:

1/3 is a more complete version of a patch I posted earlier [1]. It
makes sure that worktree.* config keys are stored in a different place
than $GIT_DIR/config. This allows us to work around the core.worktree
issue in multiple worktree setting.

I think 1/3 and 2/3 are fine. 3/3 is probably not. It's more of a
proof of concept. The tests pass, but there's no migration path for
existing submodules. Submodules modified by new Git will confuse old
Git because the old ones do not understand worktree.path (the
replacement for core.worktree)


Yeah, breaking every submodule cloned with v1.7.9 or newer is not
the way to go. You'd still have to support the old configuration
name for a very long time.

I wonder if it's worth all the hassle to invent new names. Wouldn't
it be much better to just keep a list of per-worktree configuration
value names and use that inside the config code to decide where to
find them for multiple work trees. That would also work easily for
stuff like EOL-config and would push the complexity in the config
machinery and not onto the user.
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 07/11] commit: avoid race when creating orphan commits

2015-02-08 Thread Michael Haggerty
If, during the initial check, HEAD doesn't point at anything, then we
should make sure that it *still* doesn't point at anything when we are
ready to update the reference. Otherwise, another process might commit
while we are working (e.g., while we are waiting for the user to edit
the commit message) and we will silently overwrite it.

This fixes a failing test in t7516.

Signed-off-by: Michael Haggerty mhag...@alum.mit.edu
---
 builtin/commit.c| 2 +-
 t/t7516-commit-races.sh | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 5654abd..c566e32 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1772,7 +1772,7 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
if (!transaction ||
ref_transaction_update(transaction, HEAD, sha1,
   current_head
-  ? current_head-object.sha1 : NULL,
+  ? current_head-object.sha1 : null_sha1,
   0, sb.buf, err) ||
ref_transaction_commit(transaction, err)) {
rollback_index_files();
diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh
index 5efa351..17a139e 100755
--- a/t/t7516-commit-races.sh
+++ b/t/t7516-commit-races.sh
@@ -17,7 +17,7 @@ test_expect_success 'set up editor' '
chmod +x editor
 '
 
-test_expect_failure 'race to create orphan commit' '
+test_expect_success 'race to create orphan commit' '
test_must_fail env EDITOR=./editor git commit --allow-empty 
git show -s --pretty=format:%s subject 
grep -q hare subject 
-- 
2.1.4

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] merge-file: correctly open files when in a subdir

2015-02-08 Thread Aleksander Boruch-Gruszecki
run_setup_gently() is called before merge-file. This may result in changing
current working directory, which wasn't taken into account when opening a file
for writing.

Fix by prepending the passed prefix. Previous var is left so that error
messages keep referring to the file from the user's working directory
perspective.

Signed-off-by: Aleksander Boruch-Gruszecki
aleksander.boruchgrusze...@gmail.com
---
I'm sorry if I'm sending the copies to wrong people, but I had no idea
who to send them to since there were barely any changes to the file.
Since the fix is so trivial I didn't add any tests, but I can create
some if they're needed.

 builtin/merge-file.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/merge-file.c b/builtin/merge-file.c
index 844f84f..232b768 100644
--- a/builtin/merge-file.c
+++ b/builtin/merge-file.c
@@ -90,7 +90,8 @@ int cmd_merge_file(int argc, const char **argv,
const char *prefix)

 if (ret = 0) {
 const char *filename = argv[0];
-FILE *f = to_stdout ? stdout : fopen(filename, wb);
+const char *fpath = prefix_filename(prefix, prefixlen, argv[0]);
+FILE *f = to_stdout ? stdout : fopen(fpath, wb);

 if (!f)
 ret = error(Could not open %s for writing, filename);
-- 
1.9.1
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] config: do not ungetc EOF

2015-02-08 Thread Heiko Voigt
On Thu, Feb 05, 2015 at 04:28:47PM -0500, Jeff King wrote:
 On Thu, Feb 05, 2015 at 01:16:36PM -0800, Junio C Hamano wrote:
 
  Jeff King p...@peff.net writes:
  
   On Thu, Feb 05, 2015 at 01:53:27AM -0500, Jeff King wrote:
  
   I also notice that config_buf_ungetc does not actually ungetc the
   character we give it; it just rewinds one character in the stream. This
   is fine, because we always feed the last-retrieved character. I dunno if
   it is worth fixing (it also would have fixed this infinite loop, but for
   the wrong reason; we would have stuck -1 back into the stream, and
   retrieved it on the next fgetc rather than the same '\r' over and over).
  
   Here's a patch to deal with that. I'm not sure if it's worth doing or
   not.
  
  I am not sure, either.  If this were to become stdio emulator over
  random in-core data used throughout the system, perhaps.
  
  But in its current form it is tied to the implementation of config.c
  very strongly, so...
 
 Yeah, that was my thinking, and why I have doubts. Maybe a comment would
 make more sense, like the patch below. I am also OK with just leaving
 it as-is.
 
 -- 8 --
 Subject: [PATCH] config_buf_ungetc: document quirks in a comment

I think a comment would be fine as well. Both helps to quickly find the
cause why our ungetc() might not behave as the caller expects. But I
think one of both would be good so we document that this behavior is in
fact intentional.

Cheers Heiko
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] config: do not ungetc EOF

2015-02-08 Thread Heiko Voigt
On Thu, Feb 05, 2015 at 04:00:24PM -0500, Jeff King wrote:
 On Thu, Feb 05, 2015 at 01:53:27AM -0500, Jeff King wrote:
 
  I also notice that config_buf_ungetc does not actually ungetc the
  character we give it; it just rewinds one character in the stream. This
  is fine, because we always feed the last-retrieved character. I dunno if
  it is worth fixing (it also would have fixed this infinite loop, but for
  the wrong reason; we would have stuck -1 back into the stream, and
  retrieved it on the next fgetc rather than the same '\r' over and over).
 
 Here's a patch to deal with that. I'm not sure if it's worth doing or
 not.
 
 -- 8 --
 Subject: [PATCH] config_buf_ungetc: warn when pushing back a random character

Thanks for noticing and fixing both. I think it is worth adding this
assertion. If someone in the future comes along and uses our fake
ungetc() wrong it might save some trouble figuring out whats wrong.

Cheers Heiko
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


git 2.2.2 annotate crash (strbuf.c:32)

2015-02-08 Thread Dilyan Palauzov

Hello,

I use git 2.2.2 and on my system git annotate crashed with the following 
log.


Kind regards
  Dilyan

(gdb) bt full
#0  0x7fe420649655 in raise () from /lib64/libc.so.6
No symbol table info available.
#1  0x7fe42064aad8 in abort () from /lib64/libc.so.6
No symbol table info available.
#2  0x7fe42068928b in __libc_message () from /lib64/libc.so.6
No symbol table info available.
#3  0x7fe42068ee36 in malloc_printerr () from /lib64/libc.so.6
No symbol table info available.
#4  0x7fe42068fbb3 in _int_free () from /lib64/libc.so.6
No symbol table info available.
#5  0x0041335a in strbuf_release (sb=0x7fff44797480) at strbuf.c:32
sb = 0x7fff44797480
#6  commit_info_destroy (ci=0x7fff447973e0) at builtin/blame.c:1646
No locals.
#7  find_alignment (option=0x7e8220 output_option, sb=0x7fff447972f0) 
at builtin/blame.c:2117

suspect = 0xd71230
ci = {
  author = {
alloc = 0,
len = 0,
buf = 0x810540 strbuf_slopbuf 
  },
  author_mail = {
alloc = 0,
len = 0,
buf = 0x810540 strbuf_slopbuf 
  },
  author_time = 959115099,
  author_tz = {
alloc = 0,
len = 0,
buf = 0x810540 strbuf_slopbuf 
  },
  committer = {
alloc = 0,
len = 0,
buf = 0x810540 strbuf_slopbuf 
  },
  committer_mail = {
alloc = 0,
len = 0,
buf = 0x810540 strbuf_slopbuf 
  },
  committer_time = 959115099,
  committer_tz = {
alloc = 0,
len = 0,
buf = 0x810540 strbuf_slopbuf 
  },
  summary = {
alloc = 26,
len = 25,
buf = 0xe05b50 incomplete sequence \330
  }
}
num = optimized out
longest_src_lines = 13
largest_score = 175
e = 0xd45c80
longest_dst_lines = 12
compute_auto_abbrev = 1
auto_abbrev = 7
#8  cmd_blame (argc=optimized out, argv=optimized out, 
prefix=prefix@entry=0x0) at builtin/blame.c:2797

revs = {
  commits = 0xd222a0,
  pending = {
nr = 0,
alloc = 0,
objects = 0x0
  },
  boundary_commits = {
nr = 0,
alloc = 0,
objects = 0x0
  },
  cmdline = {
nr = 0,
alloc = 0,
rev = 0x0
  },
  ref_excludes = 0x0,
  prefix = 0x0,
  def = 0x0,
  prune_data = {
_raw = 0x0,
nr = 0,
has_wildcard = 0,
recursive = 0,
magic = 0,
max_depth = 0,
items = 0x0
  },
  sort_order = REV_SORT_IN_GRAPH_ORDER,
  early_output = 0,
  ignore_missing = 0,
  ignore_missing_links = 0,
  dense = 1,
  prune = 0,
  no_walk = 0,
  show_all = 0,
  remove_empty_trees = 0,
  simplify_history = 1,
  topo_order = 0,
  simplify_merges = 0,
  simplify_by_decoration = 0,
  tag_objects = 0,
  tree_objects = 0,
  blob_objects = 0,
  verify_objects = 0,
  edge_hint = 0,
  limited = 0,
  unpacked = 0,
  boundary = 0,
  count = 0,
  left_right = 0,
  left_only = 0,
  right_only = 0,
  rewrite_parents = 0,
  print_parents = 0,
  show_source = 0,
  show_decorations = 0,
  reverse = 0,
  reverse_output_stage = 0,
  cherry_pick = 0,
  cherry_mark = 0,
  bisect = 0,
  ancestry_path = 0,
  first_parent_only = 0,
  line_level_traverse = 0,
  diff = 0,
  full_diff = 0,
  show_root_diff = 0,
  no_commit_id = 0,
  verbose_header = 0,
  ignore_merges = 1,
  combine_merges = 0,
  dense_combined_merges = 0,
  always_show_header = 0,
  shown_one = 0,
  shown_dashes = 0,
  show_merge = 0,
  show_notes = 0,
  show_notes_given = 0,
  show_signature = 0,
  pretty_given = 0,
  abbrev_commit = 0,
  abbrev_commit_given = 0,
  use_terminator = 0,
  missing_newline = 0,
  date_mode_explicit = 0,
  preserve_subject = 0,
  disable_stdin = 1,
  leak_pending = 0,
  track_linear = 0,
  track_first_time = 0,
  linear = 0,
  date_mode = DATE_ISO8601,
  abbrev = 7,
  commit_format = CMIT_FMT_MEDIUM,
  loginfo = 0x0,
  nr = 0,
  total = 0,
  mime_boundary = 0x0,
  patch_suffix = 0x0,
  numbered_files = 0,
  reroll_count = 0,
  message_id = 0x0,
  

[PATCH] sha1_file.c: make sure open_sha1_file does not open a directory

2015-02-08 Thread Kyle J. McKay
Since sha1_file: fix iterating loose alternate objects, it's possible
for the base member of the alt_odb_list structure to be NUL terminated
rather than ending with a '/' when open_sha1_file is called.

Unfortunately this causes a directory to be passed to git_open_noatime
instead of a file which it happily opens and returns whereupon this
open directory file handle is then passed to mmap.

mmap does not like this and fails with an invalid argument error
at which point the pack-objects process dies prematurely.

To avoid this we preserve the last character of the base member,
set it to '/', make the git_open_noatime call and then restore
it to its previous value which allows pack-objects to function properly.

Signed-off-by: Kyle J. McKay mack...@gmail.com
---

*

While this patch can be applied without sha1_file: fix iterating
loose alternate objects you cannot even get to the failure this fixes
without first having that patch applied.

All this stuffing of a single char back and forth into a [-1] index
seems awfully kludgy to me.

However, without this fix and the previous sha1_file fix I would need
to jettison the latest stuff and revert back to 2.1.4.

I suggest that this (or another fix for the problem) go into maint
together with the sha1_file: fix iterating loose alternate objects
fix.

*

 sha1_file.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/sha1_file.c b/sha1_file.c
index f575b3cf..235f6ad8 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1491,8 +1491,11 @@ static int open_sha1_file(const unsigned char *sha1)
 
prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt-next) {
+   char c = alt-name[-1];
+   alt-name[-1] = '/';
fill_sha1_path(alt-name, sha1);
fd = git_open_noatime(alt-base);
+   alt-name[-1] = c;
if (fd = 0)
return fd;
if (most_interesting_errno == ENOENT)
--
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] Geolocation support

2015-02-08 Thread Junio C Hamano
In case I was not clear, I do not think it is likely for us to accept
a patch that mucks with object header fields with this information.
Have them in the log text and let UI interpret them.

On Sun, Feb 8, 2015 at 4:03 PM, Alessandro Di Marco d...@ethzero.com wrote:
 Junio C Hamano gits...@pobox.com writes:

 I would personally find the feature cute, but

 snip

 Wouldn't it be sufficient to treat this in a similar way as
 references to tracker entries and references to other commit objects
 in the text of the commit message body are treated by gitk and
 friends?  Just embed the information in the log text somewhere and
 teach the UI how they look like and what to do with them.

 Sorry for the delay, I've been a little busy lately :-)

 I revised the old patch and moved the location info into a pair of extra
 headers, resp. author-location and committer-location. This way location
 unaware clients do not try to interpret the extra stuff and things stay
 smooth as usual. I tried to push/clone a location aware repository
 to/from github and it seemed to work as expected (ie. location was
 retained by github servers).

 The patch is still rough; only gitk interpret the location info at the
 moment (see http://tinypic.com/r/wars40/5), but log pretty printing is
 on the way out.

 The expected location format is CITY, COUNTRY (LAT, LON). Location is
 provided by two envvars, resp. GIT_AUTHOR_LOCATION and
 GIT_COMMITTER_LOCATION, that should be updated prior committing. E.g.

 $ export GIT_AUTHOR_LOCATION=Paris, France (48.8667, 2.)
 $ export GIT_COMMITTER_LOCATION=Paris, France (48.8667, 2.)
 $ git commit -a -s -m foo

 The patch honors the committer location on amending, etc. and gitk
 reports it with a red circle on the map. Author's location is indicated
 by a red point instead. Keep in mind that the soundness of the envvars
 content is taken for grant on commit (ie. no sanity checks yet), so
 stick carefully to the above format or gitk will whine.

 Signed-off-by: Alessandro Di Marco d...@ethzero.com
 ---
  builtin/commit.c |  48 -
  commit.c |  10 +-
  commit.h |   1 +
  gitk-git/gitk| 552 
 +--
  4 files changed, 590 insertions(+), 21 deletions(-)

 diff --git a/builtin/commit.c b/builtin/commit.c
 index 7d90c35..188f424 100644
 --- a/builtin/commit.c
 +++ b/builtin/commit.c
 @@ -629,6 +629,38 @@ static int author_date_is_interesting(void)
 return author_message || force_date;
  }

 +static int location_is_safe(char *loc)
 +{
 +   if (loc) {
 +   /* FIXME: sanity checks here */
 +   }
 +
 +   return !!loc;
 +}
 +
 +static int determine_location(struct strbuf *location)
 +{
 +   char *loc;
 +
 +   if (!amend) {
 +   loc = getenv(GIT_AUTHOR_LOCATION);
 +   if (location_is_safe(loc)) {
 +   strbuf_addstr(location, author-location );
 +   strbuf_addstr(location, loc);
 +   strbuf_addch(location, '\n');
 +   }
 +   }
 +
 +   loc = getenv(GIT_COMMITTER_LOCATION);
 +   if (location_is_safe(loc)) {
 +   strbuf_addstr(location, committer-location );
 +   strbuf_addstr(location, loc);
 +   strbuf_addch(location, '\n');
 +   }
 +
 +   return 1;
 +}
 +
  static void adjust_comment_line_char(const struct strbuf *sb)
  {
 char candidates[] = #;@!$%^|:;
 @@ -1630,6 +1662,7 @@ int cmd_commit(int argc, const char **argv, const char 
 *prefix)

 struct strbuf sb = STRBUF_INIT;
 struct strbuf author_ident = STRBUF_INIT;
 +   struct strbuf location = STRBUF_INIT;
 const char *index_file, *reflog_msg;
 char *nl;
 unsigned char sha1[20];
 @@ -1669,6 +1702,11 @@ int cmd_commit(int argc, const char **argv, const char 
 *prefix)
 return 1;
 }

 +   if (!determine_location(location)) {
 +   rollback_index_files();
 +   return 1;
 +   }
 +
 /* Determine parents */
 reflog_msg = getenv(GIT_REFLOG_ACTION);
 if (!current_head) {
 @@ -1745,13 +1783,19 @@ int cmd_commit(int argc, const char **argv, const 
 char *prefix)
 }

 if (amend) {
 -   const char *exclude_gpgsig[2] = { gpgsig, NULL };
 -   extra = read_commit_extra_headers(current_head, 
 exclude_gpgsig);
 +   const char *exclude[3] = { gpgsig, committer-location, 
 NULL };
 +   extra = read_commit_extra_headers(current_head, exclude);
 } else {
 struct commit_extra_header **tail = extra;
 append_merge_tag_headers(parents, tail);
 }

 +   if (location.len  0) {
 +   extra = read_commit_extra_header_lines(location.buf,
 +   location.len, NULL, extra);
 +   }
 +   strbuf_release(location);
 +
 if 

Re: [PATCH] Geolocation support

2015-02-08 Thread Alessandro Di Marco
Junio C Hamano gits...@pobox.com writes:

 I would personally find the feature cute, but

snip

 Wouldn't it be sufficient to treat this in a similar way as
 references to tracker entries and references to other commit objects
 in the text of the commit message body are treated by gitk and
 friends?  Just embed the information in the log text somewhere and
 teach the UI how they look like and what to do with them.

Sorry for the delay, I've been a little busy lately :-)

I revised the old patch and moved the location info into a pair of extra
headers, resp. author-location and committer-location. This way location
unaware clients do not try to interpret the extra stuff and things stay
smooth as usual. I tried to push/clone a location aware repository
to/from github and it seemed to work as expected (ie. location was
retained by github servers).

The patch is still rough; only gitk interpret the location info at the
moment (see http://tinypic.com/r/wars40/5), but log pretty printing is
on the way out.

The expected location format is CITY, COUNTRY (LAT, LON). Location is
provided by two envvars, resp. GIT_AUTHOR_LOCATION and
GIT_COMMITTER_LOCATION, that should be updated prior committing. E.g.

$ export GIT_AUTHOR_LOCATION=Paris, France (48.8667, 2.)
$ export GIT_COMMITTER_LOCATION=Paris, France (48.8667, 2.)
$ git commit -a -s -m foo

The patch honors the committer location on amending, etc. and gitk
reports it with a red circle on the map. Author's location is indicated
by a red point instead. Keep in mind that the soundness of the envvars
content is taken for grant on commit (ie. no sanity checks yet), so
stick carefully to the above format or gitk will whine.

Signed-off-by: Alessandro Di Marco d...@ethzero.com
---
 builtin/commit.c |  48 -
 commit.c |  10 +-
 commit.h |   1 +
 gitk-git/gitk| 552 +--
 4 files changed, 590 insertions(+), 21 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 7d90c35..188f424 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -629,6 +629,38 @@ static int author_date_is_interesting(void)
return author_message || force_date;
 }

+static int location_is_safe(char *loc)
+{
+   if (loc) {
+   /* FIXME: sanity checks here */
+   }
+
+   return !!loc;
+}
+
+static int determine_location(struct strbuf *location)
+{
+   char *loc;
+
+   if (!amend) {
+   loc = getenv(GIT_AUTHOR_LOCATION);
+   if (location_is_safe(loc)) {
+   strbuf_addstr(location, author-location );
+   strbuf_addstr(location, loc);
+   strbuf_addch(location, '\n');
+   }
+   }
+
+   loc = getenv(GIT_COMMITTER_LOCATION);
+   if (location_is_safe(loc)) {
+   strbuf_addstr(location, committer-location );
+   strbuf_addstr(location, loc);
+   strbuf_addch(location, '\n');
+   }
+
+   return 1;
+}
+
 static void adjust_comment_line_char(const struct strbuf *sb)
 {
char candidates[] = #;@!$%^|:;
@@ -1630,6 +1662,7 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)

struct strbuf sb = STRBUF_INIT;
struct strbuf author_ident = STRBUF_INIT;
+   struct strbuf location = STRBUF_INIT;
const char *index_file, *reflog_msg;
char *nl;
unsigned char sha1[20];
@@ -1669,6 +1702,11 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
return 1;
}

+   if (!determine_location(location)) {
+   rollback_index_files();
+   return 1;
+   }
+
/* Determine parents */
reflog_msg = getenv(GIT_REFLOG_ACTION);
if (!current_head) {
@@ -1745,13 +1783,19 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
}

if (amend) {
-   const char *exclude_gpgsig[2] = { gpgsig, NULL };
-   extra = read_commit_extra_headers(current_head, exclude_gpgsig);
+   const char *exclude[3] = { gpgsig, committer-location, NULL 
};
+   extra = read_commit_extra_headers(current_head, exclude);
} else {
struct commit_extra_header **tail = extra;
append_merge_tag_headers(parents, tail);
}

+   if (location.len  0) {
+   extra = read_commit_extra_header_lines(location.buf,
+   location.len, NULL, extra);
+   }
+   strbuf_release(location);
+
if (commit_tree_extended(sb.buf, sb.len, active_cache_tree-sha1,
 parents, sha1, author_ident.buf, sign_commit, extra)) {
rollback_index_files();
diff --git a/commit.c b/commit.c
index a8c7577..0b4e66e 100644
--- a/commit.c
+++ b/commit.c
@@ -12,8 +12,6 @@
 #include prio-queue.h
 #include sha1-lookup.h

-static struct commit_extra_header 

[PATCH 1/2] for_each_loose_file_in_objdir: take an optional strbuf path

2015-02-08 Thread Jeff King
We feed a root objdir path to this iterator function,
which then copies the result into a strbuf, so that it can
repeatedly append the object sub-directories to it. Let's
make it easy for callers to just pass us a strbuf in the
first place.

We leave the original interface as a convenience for callers
who want to just pass a const string like the result of
get_object_directory().

Signed-off-by: Jeff King p...@peff.net
---
 cache.h |  9 +
 sha1_file.c | 31 +--
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/cache.h b/cache.h
index 51ee856..4743f7e 100644
--- a/cache.h
+++ b/cache.h
@@ -1256,6 +1256,10 @@ extern int unpack_object_header(struct packed_git *, 
struct pack_window **, off_
  *
  * Any callback that is NULL will be ignored. Callbacks returning non-zero
  * will end the iteration.
+ *
+ * In the buf variant, path is a strbuf which will also be used as a
+ * scratch buffer, but restored to its original contents before
+ * the function returns.
  */
 typedef int each_loose_object_fn(const unsigned char *sha1,
 const char *path,
@@ -1271,6 +1275,11 @@ int for_each_loose_file_in_objdir(const char *path,
  each_loose_cruft_fn cruft_cb,
  each_loose_subdir_fn subdir_cb,
  void *data);
+int for_each_loose_file_in_objdir_buf(struct strbuf *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data);
 
 /*
  * Iterate over loose and packed objects in both the local
diff --git a/sha1_file.c b/sha1_file.c
index c632641..725de7f 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -3358,31 +3358,42 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
return r;
 }
 
-int for_each_loose_file_in_objdir(const char *path,
+int for_each_loose_file_in_objdir_buf(struct strbuf *path,
each_loose_object_fn obj_cb,
each_loose_cruft_fn cruft_cb,
each_loose_subdir_fn subdir_cb,
void *data)
 {
-   struct strbuf buf = STRBUF_INIT;
-   size_t baselen;
+   size_t baselen = path-len;
int r = 0;
int i;
 
-   strbuf_addstr(buf, path);
-   strbuf_addch(buf, '/');
-   baselen = buf.len;
-
for (i = 0; i  256; i++) {
-   strbuf_addf(buf, %02x, i);
-   r = for_each_file_in_obj_subdir(i, buf, obj_cb, cruft_cb,
+   strbuf_addf(path, /%02x, i);
+   r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb,
subdir_cb, data);
-   strbuf_setlen(buf, baselen);
+   strbuf_setlen(path, baselen);
if (r)
break;
}
 
+   return r;
+}
+
+int for_each_loose_file_in_objdir(const char *path,
+ each_loose_object_fn obj_cb,
+ each_loose_cruft_fn cruft_cb,
+ each_loose_subdir_fn subdir_cb,
+ void *data)
+{
+   struct strbuf buf = STRBUF_INIT;
+   int r;
+
+   strbuf_addstr(buf, path);
+   r = for_each_loose_file_in_objdir_buf(buf, obj_cb, cruft_cb,
+ subdir_cb, data);
strbuf_release(buf);
+
return r;
 }
 
-- 
2.3.0.rc1.287.g761fd19

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: Fwd: segmentation fault 11

2015-02-08 Thread Jeff King
On Sun, Feb 08, 2015 at 06:56:14PM +0100, David Krmpotic wrote:

 I updated to 2.3.0 on my OSX 10.9.5 and when trying to push to github
 (git push -u origin master), I get:
 
 Segmentation fault: 11
 
 Switched back to 1.8.5.5 and it works...

Is this reproducible easily? If so, can you do one or both of:

  1. Get a backtrace by running git-push under gdb (and/or loading gdb
 on the core file if you have it). The report you linked mentions
 that we were in 0x000102b70ebe cmd_push + 2466, but without
 having your binary, that only gives a vague sense of the line
 that caused the problem.

  2. Bisect the bug between v1.8.5.5 and v2.3.0. I can walk you through
 the specific steps if you've never bisected before.

Thanks.

-Peff
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: git 2.2.2 annotate crash (strbuf.c:32)

2015-02-08 Thread Jeff King
On Sun, Feb 08, 2015 at 10:33:40PM +0100, Dilyan Palauzov wrote:

 I use git 2.2.2 and on my system git annotate crashed with the following
 log.

I couldn't reproduce it with a few simple examples. Is it possible for
you to show us the repository and command that caused this?

 (gdb) bt full
 #0  0x7fe420649655 in raise () from /lib64/libc.so.6
 No symbol table info available.
 #1  0x7fe42064aad8 in abort () from /lib64/libc.so.6
 No symbol table info available.
 #2  0x7fe42068928b in __libc_message () from /lib64/libc.so.6
 No symbol table info available.
 #3  0x7fe42068ee36 in malloc_printerr () from /lib64/libc.so.6
 No symbol table info available.
 #4  0x7fe42068fbb3 in _int_free () from /lib64/libc.so.6
 No symbol table info available.
 #5  0x0041335a in strbuf_release (sb=0x7fff44797480) at strbuf.c:32
 sb = 0x7fff44797480

So presumably the sb-buf we pass to free() is not valid. Given the
address of the strbuf, and the address of the commit_info here:

 #6  commit_info_destroy (ci=0x7fff447973e0) at builtin/blame.c:1646
 No locals.

we are at offset 160, which is almost certainly the ci-summary strbuf.
Which is:

   summary = {
 alloc = 26,
 len = 25,
 buf = 0xe05b50 incomplete sequence \330
   }

The alloc/len look reasonable, but the buffer looks odd (it should
probably have some actual text in it). I don't see anywhere in the code
that we assign to that buffer or do anything questionable, though. We
just strbuf_add and then eventually strbuf_release it. You cannot even
get into this situation by calling strbuf_release twice, as it sets the
pointer to a known value after it is freed.

-Peff
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 2/2] sha1_file: fix iterating loose alternate objects

2015-02-08 Thread Jeff King
From: Jonathon Mah m...@jonathonmah.com

The string in 'base' contains a path suffix to a specific object;
when its value is used, the suffix must either be filled (as in
stat_sha1_file, open_sha1_file, check_and_freshen_nonlocal) or
cleared (as in prepare_packed_git) to avoid junk at the end.

660c889e (sha1_file: add for_each iterators for loose and packed
objects, 2014-10-15) introduced loose_from_alt_odb(), but this did
neither and treated 'base' as a complete path to the base object
directory, instead of a pointer to the base of the full path
string.

The trailing path after 'base' is still initialized to NUL, hiding
the bug in some common cases.  Additionally the descendent
for_each_file_in_obj_subdir() function swallows ENOENT, so an error
only shows if the alternate's path was last filled with a valid
object (where statting /path/to/existing/00/0bjectfile/00 fails).

Signed-off-by: Jonathon Mah m...@jonathonmah.com
Helped-by: Kyle J. McKay mack...@gmail.com
Signed-off-by: Jeff King p...@peff.net
---
I think the S-O-B should still stand, as the code is now a mix of our
work, and the tests are still Jonathon's. But let me know if you do not
want your name attached to this. ;)

I am also happy to build it as a patch on top of the original if that is
simpler.

 sha1_file.c  | 13 ++---
 t/t5304-prune.sh |  8 
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/sha1_file.c b/sha1_file.c
index 725de7f..a41cc4f 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -3406,9 +3406,16 @@ static int loose_from_alt_odb(struct 
alternate_object_database *alt,
  void *vdata)
 {
struct loose_alt_odb_data *data = vdata;
-   return for_each_loose_file_in_objdir(alt-base,
-data-cb, NULL, NULL,
-data-data);
+   struct strbuf buf = STRBUF_INIT;
+   int r;
+
+   /* copy base not including trailing '/' */
+   strbuf_add(buf, alt-base, alt-name - alt-base - 1);
+   r = for_each_loose_file_in_objdir_buf(buf,
+ data-cb, NULL, NULL,
+ data-data);
+   strbuf_release(buf);
+   return r;
 }
 
 int for_each_loose_object(each_loose_object_fn cb, void *data)
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index e32e46d..0794d33 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -253,4 +253,12 @@ test_expect_success 'prune .git/shallow' '
test_path_is_missing .git/shallow
 '
 
+test_expect_success 'prune: handle alternate object database' '
+   test_create_repo A 
+   git -C A commit --allow-empty -m initial commit 
+   git clone --shared A B 
+   git -C B commit --allow-empty -m next commit 
+   git -C B prune
+'
+
 test_done
-- 
2.3.0.rc1.287.g761fd19
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Высококачественный кофе от швейцарского производителя

2015-02-08 Thread Голубева Христина
Предлагаю высококачественный кофе от швейцарского производителя 
http://bit.ly/1xqBJAo

--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: git 2.2.2 annotate crash (strbuf.c:32)

2015-02-08 Thread Eric Sunshine
On Sun, Feb 8, 2015 at 8:28 PM, Jeff King p...@peff.net wrote:
 On Sun, Feb 08, 2015 at 10:33:40PM +0100, Dilyan Palauzov wrote:

 I use git 2.2.2 and on my system git annotate crashed with the following
 log.

 I couldn't reproduce it with a few simple examples. Is it possible for
 you to show us the repository and command that caused this?

I also was unable to reproduce on either Mac OS X or Linux with git
2.2.2. Clues from the traceback suggest the cyrus-imapd project and
annotation of timsieved/parser.c. I tried:

  git clone git://git.cyrusimap.org/cyrus-imapd/
  cd cyrus-imapd
  git --no-pager annotate timsieved/parser.c
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] sha1_file.c: make sure open_sha1_file does not open a directory

2015-02-08 Thread Jeff King
On Sun, Feb 08, 2015 at 07:54:44PM -0500, Jeff King wrote:

 However, the first thing for_each_loose_file_in_objdir is going to do is
 stick the path into a strbuf. So perhaps the most sensible thing is to
 just teach it to take a strbuf from the caller. I'll work up a patch.
 
 It looks like a1b47246 isn't even in next yet, so I'll build it
 directly on what is already in master, dropping Jonathan's patch.

Here it is. The first patch is a refactoring to allow this,
and the second is the moral equivalent of Jonathon's patch.
These replace a1b47246 on the tip of jk/prune-mtime.

  [1/2]: for_each_loose_file_in_objdir: take an optional strbuf path
  [2/2]: sha1_file: fix iterating loose alternate objects

-Peff
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] sha1_file.c: make sure open_sha1_file does not open a directory

2015-02-08 Thread Jeff King
On Sun, Feb 08, 2015 at 03:05:32PM -0800, Kyle J. McKay wrote:

 Since sha1_file: fix iterating loose alternate objects, it's possible
 for the base member of the alt_odb_list structure to be NUL terminated
 rather than ending with a '/' when open_sha1_file is called.

Good catch. Users of struct alternate_object_database expect to be
able to fill in the name field, and have a full path in the base
field. That is part of the contract of the struct, and the recent fix
does not live up to that contract inside the for_each_loose_file...
callbacks.

For that reason, I don't think your fix is complete. It fixes _one_
caller to work around this breakage of the contract, but it does not do
anything about the other callers (of which you can find several if you
grep for `fill_sha1_path`). I don't know if those can be hit from this
code path, but it does not matter. We jump to a callback with the NUL
set, so we must assume any arbitrary code can be run.

So either we must amend the contract, so that users of alt-base must
check the termination themselves (i.e., your patch, but extended to all
users of alt-base), or we have to fix for_each_loose_file not to leave
the alt_odb struct in such a broken state. I think I'd prefer the
latter.

 While this patch can be applied without sha1_file: fix iterating
 loose alternate objects you cannot even get to the failure this fixes
 without first having that patch applied.

Right. This is literally a bug introduced by that patch. It's OK to
munge alt-name[-1] temporarily, but you have to make sure you are not
calling functions which will look at it while it is munged. The way
refs_from_alternate_cb does it is OK (NUL-terminate, xstrdup, then fix
it; or just xmemdupz the correct length, which we know from alt-name).

Something like:

diff --git a/sha1_file.c b/sha1_file.c
index 9e0c271..7253213 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -3396,11 +3396,11 @@ static int loose_from_alt_odb(struct 
alternate_object_database *alt,
 {
struct loose_alt_odb_data *data = vdata;
int r;
-   alt-name[-1] = 0;
-   r = for_each_loose_file_in_objdir(alt-base,
+   char *buf = xmemdupz(alt-base, alt-name - alt-base - 1);
+   r = for_each_loose_file_in_objdir(buf,
  data-cb, NULL, NULL,
  data-data);
-   alt-name[-1] = '/';
+   free(buf);
return r;
 }
 

However, the first thing for_each_loose_file_in_objdir is going to do is
stick the path into a strbuf. So perhaps the most sensible thing is to
just teach it to take a strbuf from the caller. I'll work up a patch.

It looks like a1b47246 isn't even in next yet, so I'll build it
directly on what is already in master, dropping Jonathan's patch.

-Peff
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html