Code

Merge branch 'sb/maint-1.6.0-add-config-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 22 Jun 2009 07:44:09 +0000 (00:44 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 22 Jun 2009 07:44:09 +0000 (00:44 -0700)
* sb/maint-1.6.0-add-config-fix:
  add: allow configurations to be overriden by command line

1  2 
builtin-add.c
t/t3700-add.sh

diff --combined builtin-add.c
index ad889aac5bd174bf96a87b78eeb243aea89a1626,13db4a6455e17a0f960e53ddcbc6e2db898f55d0..d6ad57bfc2841959a2017d0723c1f9d57a920fac
@@@ -8,6 -8,10 +8,6 @@@
  #include "dir.h"
  #include "exec_cmd.h"
  #include "cache-tree.h"
 -#include "diff.h"
 -#include "diffcore.h"
 -#include "commit.h"
 -#include "revision.h"
  #include "run-command.h"
  #include "parse-options.h"
  
@@@ -15,30 -19,9 +15,30 @@@ static const char * const builtin_add_u
        "git add [options] [--] <filepattern>...",
        NULL
  };
 -static int patch_interactive = 0, add_interactive = 0;
 +static int patch_interactive, add_interactive;
  static int take_worktree_changes;
  
 +static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
 +{
 +      int num_unmatched = 0, i;
 +
 +      /*
 +       * Since we are walking the index as if we were walking the directory,
 +       * we have to mark the matched pathspec as seen; otherwise we will
 +       * mistakenly think that the user gave a pathspec that did not match
 +       * anything.
 +       */
 +      for (i = 0; i < specs; i++)
 +              if (!seen[i])
 +                      num_unmatched++;
 +      if (!num_unmatched)
 +              return;
 +      for (i = 0; i < active_nr; i++) {
 +              struct cache_entry *ce = active_cache[i];
 +              match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
 +      }
 +}
 +
  static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
  {
        char *seen;
                        *dst++ = entry;
        }
        dir->nr = dst - dir->entries;
 +      fill_pathspec_matches(pathspec, seen, specs);
  
        for (i = 0; i < specs; i++) {
 -              if (!seen[i] && !file_exists(pathspec[i]))
 +              if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i]))
                        die("pathspec '%s' did not match any files",
                                        pathspec[i]);
        }
          free(seen);
  }
  
 +static void treat_gitlinks(const char **pathspec)
 +{
 +      int i;
 +
 +      if (!pathspec || !*pathspec)
 +              return;
 +
 +      for (i = 0; i < active_nr; i++) {
 +              struct cache_entry *ce = active_cache[i];
 +              if (S_ISGITLINK(ce->ce_mode)) {
 +                      int len = ce_namelen(ce), j;
 +                      for (j = 0; pathspec[j]; j++) {
 +                              int len2 = strlen(pathspec[j]);
 +                              if (len2 <= len || pathspec[j][len] != '/' ||
 +                                  memcmp(ce->name, pathspec[j], len))
 +                                      continue;
 +                              if (len2 == len + 1)
 +                                      /* strip trailing slash */
 +                                      pathspec[j] = xstrndup(ce->name, len);
 +                              else
 +                                      die ("Path '%s' is in submodule '%.*s'",
 +                                              pathspec[j], len, ce->name);
 +                      }
 +              }
 +      }
 +}
 +
  static void fill_directory(struct dir_struct *dir, const char **pathspec,
                int ignored_too)
  {
        /* Set up the default git porcelain excludes */
        memset(dir, 0, sizeof(*dir));
        if (!ignored_too) {
 -              dir->collect_ignored = 1;
 +              dir->flags |= DIR_COLLECT_IGNORED;
                setup_standard_excludes(dir);
        }
  
                prune_directory(dir, pathspec, baselen);
  }
  
 -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_cache(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_PRETEND))
 -                              remove_file_from_cache(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;
 -}
 -
  static void refresh(int verbose, const char **pathspec)
  {
        char *seen;
@@@ -145,16 -153,6 +145,16 @@@ static const char **validate_pathspec(i
  {
        const char **pathspec = get_pathspec(prefix, argv);
  
 +      if (pathspec) {
 +              const char **p;
 +              for (p = pathspec; *p; p++) {
 +                      if (has_symlink_leading_path(*p, strlen(*p))) {
 +                              int len = prefix ? strlen(prefix) : 0;
 +                              die("'%s' is beyond a symbolic link", *p + len);
 +                      }
 +              }
 +      }
 +
        return pathspec;
  }
  
@@@ -193,7 -191,7 +193,7 @@@ static const char ignore_error[] 
  "The following paths are ignored by one of your .gitignore files:\n";
  
  static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 -static int ignore_add_errors, addremove;
 +static int ignore_add_errors, addremove, intent_to_add;
  
  static struct option builtin_add_options[] = {
        OPT__DRY_RUN(&show_only),
        OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
        OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"),
        OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
 +      OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
        OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
        OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
        OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
@@@ -250,6 -247,8 +250,8 @@@ int cmd_add(int argc, const char **argv
        int add_new_files;
        int require_pathspec;
  
+       git_config(add_config, NULL);
        argc = parse_options(argc, argv, builtin_add_options,
                          builtin_add_usage, 0);
        if (patch_interactive)
        if (add_interactive)
                exit(interactive_add(argc, argv, prefix));
  
-       git_config(add_config, NULL);
        if (addremove && take_worktree_changes)
                die("-A and -u are mutually incompatible");
 -      if (addremove && !argc) {
 +      if ((addremove || take_worktree_changes) && !argc) {
                static const char *here[2] = { ".", NULL };
                argc = 1;
                argv = here;
  
        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
                 (show_only ? ADD_CACHE_PRETEND : 0) |
 -               (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
 +               (intent_to_add ? ADD_CACHE_INTENT : 0) |
 +               (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
 +               (!(addremove || take_worktree_changes)
 +                ? ADD_CACHE_IGNORE_REMOVAL : 0));
  
        if (require_pathspec && argc == 0) {
                fprintf(stderr, "Nothing specified, nothing added.\n");
                fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
                return 0;
        }
 -      pathspec = get_pathspec(prefix, argv);
 -
 -      /*
 -       * If we are adding new files, we need to scan the working
 -       * tree to find the ones that match pathspecs; this needs
 -       * to be done before we read the index.
 -       */
 -      if (add_new_files)
 -              fill_directory(&dir, pathspec, ignored_too);
 +      pathspec = validate_pathspec(argc, argv, prefix);
  
        if (read_cache() < 0)
                die("index file corrupt");
 +      treat_gitlinks(pathspec);
 +
 +      if (add_new_files)
 +              /* This picks up the paths that are not tracked */
 +              fill_directory(&dir, pathspec, ignored_too);
  
        if (refresh_only) {
                refresh(verbose, pathspec);
                goto finish;
        }
  
 -      if (take_worktree_changes || addremove)
 -              exit_status |= add_files_to_cache(prefix, pathspec, flags);
 +      exit_status |= add_files_to_cache(prefix, pathspec, flags);
  
        if (add_new_files)
                exit_status |= add_files(&dir, flags);
diff --combined t/t3700-add.sh
index 050de42ef4148a730c30520ccaad5e9871e536bd,060a6ecabe4cf208dc88e4e4b1d42464c5f4d894..32595fa30c3171495929ceecda3272b963e51bed
@@@ -30,7 -30,7 +30,7 @@@ test_expect_success 
         *) echo fail; git ls-files --stage xfoo1; (exit 1);;
         esac'
  
 -test_expect_success 'git add: filemode=0 should not get confused by symlink' '
 +test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo1 &&
        ln -s foo xfoo1 &&
        git add xfoo1 &&
@@@ -51,7 -51,7 +51,7 @@@ test_expect_success 
         *) echo fail; git ls-files --stage xfoo2; (exit 1);;
         esac'
  
 -test_expect_success 'git add: filemode=0 should not get confused by symlink' '
 +test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo2 &&
        ln -s foo xfoo2 &&
        git update-index --add xfoo2 &&
@@@ -61,7 -61,7 +61,7 @@@
        esac
  '
  
 -test_expect_success \
 +test_expect_success SYMLINKS \
        'git update-index --add: Test that executable bit is not used...' \
        'git config core.filemode 0 &&
         ln -s xfoo2 xfoo3 &&
@@@ -179,7 -179,7 +179,7 @@@ test_expect_success 'git add --refresh
        test -z "`git diff-index HEAD -- foo`"
  '
  
 -test_expect_success 'git add should fail atomically upon an unreadable file' '
 +test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' '
        git reset --hard &&
        date >foo1 &&
        date >foo2 &&
  
  rm -f foo2
  
 -test_expect_success 'git add --ignore-errors' '
 +test_expect_success POSIXPERM 'git add --ignore-errors' '
        git reset --hard &&
        date >foo1 &&
        date >foo2 &&
  
  rm -f foo2
  
 -test_expect_success 'git add (add.ignore-errors)' '
 +test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
        git config add.ignore-errors 1 &&
        git reset --hard &&
        date >foo1 &&
  '
  rm -f foo2
  
 -test_expect_success 'git add (add.ignore-errors = false)' '
 +test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
        git config add.ignore-errors 0 &&
        git reset --hard &&
        date >foo1 &&
        test_must_fail git add --verbose . &&
        ! ( git ls-files foo1 | grep foo1 )
  '
+ rm -f foo2
+ test_expect_success '--no-ignore-errors overrides config' '
+        git config add.ignore-errors 1 &&
+        git reset --hard &&
+        date >foo1 &&
+        date >foo2 &&
+        chmod 0 foo2 &&
+        test_must_fail git add --verbose --no-ignore-errors . &&
+        ! ( git ls-files foo1 | grep foo1 ) &&
+        git config add.ignore-errors 0
+ '
+ rm -f foo2
  
 -test_expect_success 'git add '\''fo\[ou\]bar'\'' ignores foobar' '
 +test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
        git reset --hard &&
        touch fo\[ou\]bar foobar &&
        git add '\''fo\[ou\]bar'\'' &&
 -      git ls-files fo\[ou\]bar | grep -F fo\[ou\]bar &&
 +      git ls-files fo\[ou\]bar | fgrep fo\[ou\]bar &&
        ! ( git ls-files foobar | grep foobar )
  '