Merge branch 'jk/maint-ls-files-other' into jk/fix-ls-files-other
authorJunio C Hamano <gitster@pobox.com>
Fri, 17 Oct 2008 20:03:52 +0000 (13:03 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 17 Oct 2008 20:03:52 +0000 (13:03 -0700)
* jk/maint-ls-files-other:
refactor handling of "other" files in ls-files and status

Conflicts:
read-cache.c

1  2 
cache.h
read-cache.c
t/t7502-status.sh
wt-status.c
diff --combined cache.h
index 991544cf0bd7e84c5db5bd6486e5922a53ec136f,76730cee61441ce694c48addf02e935237addc71..ae6647e22c0b6811c5f932b06622a75bd97b0a22
+++ b/cache.h
@@@ -6,14 -6,8 +6,14 @@@
  #include "hash.h"
  
  #include SHA1_HEADER
 -#include <zlib.h>
 +#ifndef git_SHA_CTX
 +#define git_SHA_CTX   SHA_CTX
 +#define git_SHA1_Init SHA1_Init
 +#define git_SHA1_Update       SHA1_Update
 +#define git_SHA1_Final        SHA1_Final
 +#endif
  
 +#include <zlib.h>
  #if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
  #define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
  #endif
@@@ -132,7 -126,6 +132,7 @@@ struct cache_entry 
  
  #define CE_NAMEMASK  (0x0fff)
  #define CE_STAGEMASK (0x3000)
 +#define CE_EXTENDED  (0x4000)
  #define CE_VALID     (0x8000)
  #define CE_STAGESHIFT 12
  
@@@ -277,6 -270,7 +277,7 @@@ static inline void remove_name_hash(str
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
+ #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
  #endif
  
  enum object_type {
@@@ -319,7 -313,6 +320,7 @@@ extern int is_bare_repository(void)
  extern int is_inside_git_dir(void);
  extern char *git_work_tree_cfg;
  extern int is_inside_work_tree(void);
 +extern int have_git_dir(void);
  extern const char *get_git_dir(void);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
@@@ -378,7 -371,6 +379,7 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
  #define ADD_CACHE_SKIP_DFCHECK 4      /* Ok to skip DF conflict checks */
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
 +#define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
@@@ -387,12 -379,11 +388,13 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_VERBOSE 1
  #define ADD_CACHE_PRETEND 2
  #define ADD_CACHE_IGNORE_ERRORS       4
 +#define ADD_CACHE_IGNORE_REMOVAL 8
 +#define ADD_CACHE_INTENT 16
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
+ extern int index_name_is_other(const struct index_state *, const char *, int);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
@@@ -403,6 -394,7 +405,6 @@@ extern int ie_modified(const struct ind
  
  extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 -extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
@@@ -462,7 -454,6 +464,7 @@@ enum safe_crlf 
  extern enum safe_crlf safe_crlf;
  
  enum branch_track {
 +      BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
@@@ -542,7 -533,6 +544,7 @@@ static inline int is_absolute_path(cons
  {
        return path[0] == '/' || has_dos_drive_prefix(path);
  }
 +int is_directory(const char *);
  const char *make_absolute_path(const char *path);
  const char *make_nonrelative_path(const char *path);
  const char *make_relative_path(const char *abs, const char *base);
@@@ -650,8 -640,6 +652,8 @@@ extern struct alternate_object_databas
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
  extern void add_to_alternates_file(const char *reference);
 +typedef int alt_odb_fn(struct alternate_object_database *, void *);
 +extern void foreach_alt_odb(alt_odb_fn, void*);
  
  struct pack_window {
        struct pack_window *next;
@@@ -720,11 -708,7 +722,11 @@@ extern struct child_process *git_connec
  extern int finish_connect(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  extern int get_ack(int fd, unsigned char *result_sha1);
 -extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
 +struct extra_have_objects {
 +      int nr, alloc;
 +      unsigned char (*array)[20];
 +};
 +extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1);
@@@ -758,6 -742,7 +760,6 @@@ typedef int (*config_fn_t)(const char *
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern int git_config(config_fn_t fn, void *);
 -extern int git_parse_long(const char *, long *);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
  extern unsigned long git_config_ulong(const char *, const char *);
diff --combined read-cache.c
index c229fd4d0da68b204e81e1e8127f8529abe06336,4e067e489743aca27dc13e3d04d4e41f4ffec95b..780f2c723eb0aa9ea7001ac7e6a03768c735ff74
@@@ -8,12 -8,6 +8,12 @@@
  #include "cache-tree.h"
  #include "refs.h"
  #include "dir.h"
 +#include "tree.h"
 +#include "commit.h"
 +#include "diff.h"
 +#include "diffcore.h"
 +#include "revision.h"
 +#include "blob.h"
  
  /* Index extensions.
   *
@@@ -512,14 -506,6 +512,14 @@@ static struct cache_entry *create_alias
        return new;
  }
  
 +static void record_intent_to_add(struct cache_entry *ce)
 +{
 +      unsigned char sha1[20];
 +      if (write_sha1_file("", 0, blob_type, sha1))
 +              die("cannot create an empty blob in the object database");
 +      hashcpy(ce->sha1, sha1);
 +}
 +
  int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
  {
        int size, namelen, was_same;
        unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
 +      int intent_only = flags & ADD_CACHE_INTENT;
 +      int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|
 +                        (intent_only ? ADD_CACHE_NEW_ONLY : 0));
  
        if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
                return error("%s: can only add regular files, symbolic links or git-directories", path);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
        ce->ce_flags = namelen;
 -      fill_stat_cache_info(ce, st);
 +      if (!intent_only)
 +              fill_stat_cache_info(ce, st);
  
        if (trust_executable_bit && has_symlinks)
                ce->ce_mode = create_ce_mode(st_mode);
                alias->ce_flags |= CE_ADDED;
                return 0;
        }
 -      if (index_path(ce->sha1, path, st, 1))
 -              return error("unable to index file %s", path);
 +      if (!intent_only) {
 +              if (index_path(ce->sha1, path, st, 1))
 +                      return error("unable to index file %s", path);
 +      } else
 +              record_intent_to_add(ce);
 +
        if (ignore_case && alias && different_name(ce, alias))
                ce = create_alias_ce(ce, alias);
        ce->ce_flags |= CE_ADDED;
  
        if (pretend)
                ;
 -      else if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
 +      else if (add_index_entry(istate, ce, add_option))
                return error("unable to add %s to index",path);
        if (verbose && !was_same)
                printf("add '%s'\n", path);
@@@ -608,10 -586,8 +608,10 @@@ struct cache_entry *make_cache_entry(un
        int size, len;
        struct cache_entry *ce;
  
 -      if (!verify_path(path))
 +      if (!verify_path(path)) {
 +              error("Invalid path '%s'", path);
                return NULL;
 +      }
  
        len = strlen(path);
        size = cache_entry_size(len);
@@@ -867,15 -843,13 +867,15 @@@ static int add_index_entry_with_check(s
        int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
        int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
        int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
 +      int new_only = option & ADD_CACHE_NEW_ONLY;
  
        cache_tree_invalidate_path(istate->cache_tree, ce->name);
        pos = index_name_pos(istate, ce->name, ce->ce_flags);
  
        /* existing match? Just replace it. */
        if (pos >= 0) {
 -              replace_index_entry(istate, pos, ce);
 +              if (!new_only)
 +                      replace_index_entry(istate, pos, ce);
                return 0;
        }
        pos = -pos-1;
        if (!ok_to_add)
                return -1;
        if (!verify_path(ce->name))
 -              return -1;
 +              return error("Invalid path '%s'", ce->name);
  
        if (!skip_df_check &&
            check_file_directory_conflict(istate, ce, pos, ok_to_replace)) {
@@@ -1093,16 -1067,16 +1093,16 @@@ struct cache_entry *refresh_cache_entry
  
  static int verify_hdr(struct cache_header *hdr, unsigned long size)
  {
 -      SHA_CTX c;
 +      git_SHA_CTX c;
        unsigned char sha1[20];
  
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
                return error("bad signature");
        if (hdr->hdr_version != htonl(2))
                return error("bad index version");
 -      SHA1_Init(&c);
 -      SHA1_Update(&c, hdr, size - 20);
 -      SHA1_Final(sha1, &c);
 +      git_SHA1_Init(&c);
 +      git_SHA1_Update(&c, hdr, size - 20);
 +      git_SHA1_Final(sha1, &c);
        if (hashcmp(sha1, (unsigned char *)hdr + size - 20))
                return error("bad index file sha1 signature");
        return 0;
@@@ -1144,10 -1118,6 +1144,10 @@@ static void convert_from_disk(struct on
        ce->ce_size  = ntohl(ondisk->size);
        /* On-disk flags are just 16 bits */
        ce->ce_flags = ntohs(ondisk->flags);
 +
 +      /* For future extension: we do not understand this entry yet */
 +      if (ce->ce_flags & CE_EXTENDED)
 +              die("Unknown index entry format");
        hashcpy(ce->sha1, ondisk->sha1);
  
        len = ce->ce_flags & CE_NAMEMASK;
@@@ -1299,11 -1269,11 +1299,11 @@@ int unmerged_index(const struct index_s
  static unsigned char write_buffer[WRITE_BUFFER_SIZE];
  static unsigned long write_buffer_len;
  
 -static int ce_write_flush(SHA_CTX *context, int fd)
 +static int ce_write_flush(git_SHA_CTX *context, int fd)
  {
        unsigned int buffered = write_buffer_len;
        if (buffered) {
 -              SHA1_Update(context, write_buffer, buffered);
 +              git_SHA1_Update(context, write_buffer, buffered);
                if (write_in_full(fd, write_buffer, buffered) != buffered)
                        return -1;
                write_buffer_len = 0;
        return 0;
  }
  
 -static int ce_write(SHA_CTX *context, int fd, void *data, unsigned int len)
 +static int ce_write(git_SHA_CTX *context, int fd, void *data, unsigned int len)
  {
        while (len) {
                unsigned int buffered = write_buffer_len;
        return 0;
  }
  
 -static int write_index_ext_header(SHA_CTX *context, int fd,
 +static int write_index_ext_header(git_SHA_CTX *context, int fd,
                                  unsigned int ext, unsigned int sz)
  {
        ext = htonl(ext);
                (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
  }
  
 -static int ce_flush(SHA_CTX *context, int fd)
 +static int ce_flush(git_SHA_CTX *context, int fd)
  {
        unsigned int left = write_buffer_len;
  
        if (left) {
                write_buffer_len = 0;
 -              SHA1_Update(context, write_buffer, left);
 +              git_SHA1_Update(context, write_buffer, left);
        }
  
        /* Flush first if not enough space for SHA1 signature */
        }
  
        /* Append the SHA1 signature at the end */
 -      SHA1_Final(write_buffer + left, context);
 +      git_SHA1_Final(write_buffer + left, context);
        left += 20;
        return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
  }
@@@ -1413,7 -1383,7 +1413,7 @@@ static void ce_smudge_racily_clean_entr
        }
  }
  
 -static int ce_write_entry(SHA_CTX *c, int fd, struct cache_entry *ce)
 +static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
  {
        int size = ondisk_ce_size(ce);
        struct ondisk_cache_entry *ondisk = xcalloc(1, size);
  
  int write_index(const struct index_state *istate, int newfd)
  {
 -      SHA_CTX c;
 +      git_SHA_CTX c;
        struct cache_header hdr;
        int i, err, removed;
        struct cache_entry **cache = istate->cache;
        hdr.hdr_version = htonl(2);
        hdr.hdr_entries = htonl(entries - removed);
  
 -      SHA1_Init(&c);
 +      git_SHA1_Init(&c);
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
                return -1;
  
  
        /* Write extension data here */
        if (istate->cache_tree) {
 -              struct strbuf sb;
 +              struct strbuf sb = STRBUF_INIT;
  
 -              strbuf_init(&sb, 0);
                cache_tree_write(&sb, istate->cache_tree);
                err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0
                        || ce_write(&c, newfd, sb.buf, sb.len) < 0;
@@@ -1510,58 -1481,30 +1510,85 @@@ int read_index_unmerged(struct index_st
        return !!last;
  }
  
 +struct update_callback_data
 +{
 +      int flags;
 +      int add_errors;
 +};
 +
 +static void update_callback(struct diff_queue_struct *q,
 +                          struct diff_options *opt, void *cbdata)
 +{
 +      int i;
 +      struct update_callback_data *data = cbdata;
 +
 +      for (i = 0; i < q->nr; i++) {
 +              struct diff_filepair *p = q->queue[i];
 +              const char *path = p->one->path;
 +              switch (p->status) {
 +              default:
 +                      die("unexpected diff status %c", p->status);
 +              case DIFF_STATUS_UNMERGED:
 +              case DIFF_STATUS_MODIFIED:
 +              case DIFF_STATUS_TYPE_CHANGED:
 +                      if (add_file_to_index(&the_index, path, data->flags)) {
 +                              if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
 +                                      die("updating files failed");
 +                              data->add_errors++;
 +                      }
 +                      break;
 +              case DIFF_STATUS_DELETED:
 +                      if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
 +                              break;
 +                      if (!(data->flags & ADD_CACHE_PRETEND))
 +                              remove_file_from_index(&the_index, path);
 +                      if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
 +                              printf("remove '%s'\n", path);
 +                      break;
 +              }
 +      }
 +}
 +
 +int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
 +{
 +      struct update_callback_data data;
 +      struct rev_info rev;
 +      init_revisions(&rev, prefix);
 +      setup_revisions(0, NULL, &rev, NULL);
 +      rev.prune_data = pathspec;
 +      rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
 +      rev.diffopt.format_callback = update_callback;
 +      data.flags = flags;
 +      data.add_errors = 0;
 +      rev.diffopt.format_callback_data = &data;
 +      run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 +      return !!data.add_errors;
 +}
 +
+ /*
+  * Returns 1 if the path is an "other" path with respect to
+  * the index; that is, the path is not mentioned in the index at all,
+  * either as a file, a directory with some files in the index,
+  * or as an unmerged entry.
+  *
+  * We helpfully remove a trailing "/" from directories so that
+  * the output of read_directory can be used as-is.
+  */
+ int index_name_is_other(const struct index_state *istate, const char *name,
+               int namelen)
+ {
+       int pos;
+       if (namelen && name[namelen - 1] == '/')
+               namelen--;
+       pos = index_name_pos(istate, name, namelen);
+       if (0 <= pos)
+               return 0;       /* exact match */
+       pos = -pos - 1;
+       if (pos < istate->cache_nr) {
+               struct cache_entry *ce = istate->cache[pos];
+               if (ce_namelen(ce) == namelen &&
+                   !memcmp(ce->name, name, namelen))
+                       return 0; /* Yup, this one exists unmerged */
+       }
+       return 1;
+ }
diff --combined t/t7502-status.sh
index 1905fb34cd222aa9443d28fd673b0cc9e02f3335,187a13e64fe2b2fea85b3a80852cd2dacca5acd3..93f875f50054225e0879843b6e29ac057ba6c020
@@@ -46,7 -46,6 +46,7 @@@ cat > expect << \EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -77,7 -76,6 +77,7 @@@ cat >expect <<EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -106,7 -104,6 +106,7 @@@ cat >expect <<EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -141,7 -138,6 +141,7 @@@ cat >expect <<EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -178,7 -174,6 +178,7 @@@ cat > expect << \EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   modified
  #
@@@ -209,7 -204,6 +209,7 @@@ cat > expect << \EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -273,7 -267,6 +273,7 @@@ cat >expect <<EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -292,6 -285,12 +292,12 @@@ test_expect_success 'status submodule s
        test_cmp expect output
  '
  
+ # we expect the same as the previous test
+ test_expect_success 'status --untracked-files=all does not show submodule' '
+       git status --untracked-files=all >output &&
+       test_cmp expect output
+ '
  head=$(cd sm && git rev-parse --short=7 --verify HEAD)
  
  cat >expect <<EOF
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -334,7 -332,6 +340,7 @@@ cat >expect <<EO
  # On branch master
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
@@@ -366,7 -363,6 +372,7 @@@ cat >expect <<EO
  #
  # Changed but not updated:
  #   (use "git add <file>..." to update what will be committed)
 +#   (use "git checkout -- <file>..." to discard changes in working directory)
  #
  #     modified:   dir1/modified
  #
diff --combined wt-status.c
index d2eac36aeaafab2800b4f0802c7db9656c44cdeb,64cedfcbe14ab0448ab6225b50244a45492a546a..c3a9cab8980daa38e647a13eb2f0fcb42fbfbb8e
@@@ -22,6 -22,12 +22,6 @@@ static char wt_status_colors[][COLOR_MA
        "\033[31m", /* WT_STATUS_NOBRANCH: red */
  };
  
 -static const char use_add_msg[] =
 -"use \"git add <file>...\" to update what will be committed";
 -static const char use_add_rm_msg[] =
 -"use \"git add/rm <file>...\" to update what will be committed";
 -static const char use_add_to_include_msg[] =
 -"use \"git add <file>...\" to include in what will be committed";
  enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
  
  static int parse_status_slot(const char *var, int offset)
@@@ -70,24 -76,12 +70,24 @@@ static void wt_status_print_cached_head
        color_fprintf_ln(s->fp, c, "#");
  }
  
 -static void wt_status_print_header(struct wt_status *s,
 -                                 const char *main, const char *sub)
 +static void wt_status_print_dirty_header(struct wt_status *s,
 +                                       int has_deleted)
  {
        const char *c = color(WT_STATUS_HEADER);
 -      color_fprintf_ln(s->fp, c, "# %s:", main);
 -      color_fprintf_ln(s->fp, c, "#   (%s)", sub);
 +      color_fprintf_ln(s->fp, c, "# Changed but not updated:");
 +      if (!has_deleted)
 +              color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to update what will be committed)");
 +      else
 +              color_fprintf_ln(s->fp, c, "#   (use \"git add/rm <file>...\" to update what will be committed)");
 +      color_fprintf_ln(s->fp, c, "#   (use \"git checkout -- <file>...\" to discard changes in working directory)");
 +      color_fprintf_ln(s->fp, c, "#");
 +}
 +
 +static void wt_status_print_untracked_header(struct wt_status *s)
 +{
 +      const char *c = color(WT_STATUS_HEADER);
 +      color_fprintf_ln(s->fp, c, "# Untracked files:");
 +      color_fprintf_ln(s->fp, c, "#   (use \"git add <file>...\" to include in what will be committed)");
        color_fprintf_ln(s->fp, c, "#");
  }
  
@@@ -103,8 -97,10 +103,8 @@@ static void wt_status_print_filepair(st
  {
        const char *c = color(t);
        const char *one, *two;
 -      struct strbuf onebuf, twobuf;
 +      struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
  
 -      strbuf_init(&onebuf, 0);
 -      strbuf_init(&twobuf, 0);
        one = quote_path(p->one->path, -1, &onebuf, s->prefix);
        two = quote_path(p->two->path, -1, &twobuf, s->prefix);
  
@@@ -170,14 -166,14 +170,14 @@@ static void wt_status_print_changed_cb(
        struct wt_status *s = data;
        int i;
        if (q->nr) {
 -              const char *msg = use_add_msg;
 +              int has_deleted = 0;
                s->workdir_dirty = 1;
                for (i = 0; i < q->nr; i++)
                        if (q->queue[i]->status == DIFF_STATUS_DELETED) {
 -                              msg = use_add_rm_msg;
 +                              has_deleted = 1;
                                break;
                        }
 -              wt_status_print_header(s, "Changed but not updated", msg);
 +              wt_status_print_dirty_header(s, has_deleted);
        }
        for (i = 0; i < q->nr; i++)
                wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
  static void wt_status_print_initial(struct wt_status *s)
  {
        int i;
 -      struct strbuf buf;
 +      struct strbuf buf = STRBUF_INIT;
  
 -      strbuf_init(&buf, 0);
        if (active_nr) {
                s->commitable = 1;
                wt_status_print_cached_header(s);
@@@ -265,8 -262,9 +265,8 @@@ static void wt_status_print_untracked(s
        struct dir_struct dir;
        int i;
        int shown_header = 0;
 -      struct strbuf buf;
 +      struct strbuf buf = STRBUF_INIT;
  
 -      strbuf_init(&buf, 0);
        memset(&dir, 0, sizeof(dir));
  
        if (!s->untracked) {
  
        read_directory(&dir, ".", "", 0, NULL);
        for(i = 0; i < dir.nr; i++) {
-               /* check for matching entry, which is unmerged; lifted from
-                * builtin-ls-files:show_other_files */
                struct dir_entry *ent = dir.entries[i];
-               int pos = cache_name_pos(ent->name, ent->len);
-               struct cache_entry *ce;
-               if (0 <= pos)
-                       die("bug in wt_status_print_untracked");
-               pos = -pos - 1;
-               if (pos < active_nr) {
-                       ce = active_cache[pos];
-                       if (ce_namelen(ce) == ent->len &&
-                           !memcmp(ce->name, ent->name, ent->len))
-                               continue;
-               }
+               if (!cache_name_is_other(ent->name, ent->len))
+                       continue;
                if (!shown_header) {
                        s->workdir_untracked = 1;
 -                      wt_status_print_header(s, "Untracked files",
 -                                             use_add_to_include_msg);
 +                      wt_status_print_untracked_header(s);
                        shown_header = 1;
                }
                color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");