Code

Merge branch 'cb/ignored-paths-are-precious' into pu
authorJunio C Hamano <gitster@pobox.com>
Mon, 31 Jan 2011 03:03:19 +0000 (19:03 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 31 Jan 2011 03:03:19 +0000 (19:03 -0800)
* cb/ignored-paths-are-precious:
  checkout/merge: optionally fail operation when ignored files need to be overwritten

Conflicts:
Documentation/config.txt
builtin/checkout.c

1  2 
Documentation/config.txt
builtin/checkout.c
builtin/merge.c
cache.h
config.c
environment.c

diff --combined Documentation/config.txt
index c5e183516a104e6efb7ed597fb4498d75560ab68,fdaa586f08b60758aafca7d8b01ab6cd979dd039..312293799d3731f488547b7904398b2bb16de81d
@@@ -317,26 -317,24 +317,26 @@@ false), while all other repositories ar
  = true).
  
  core.worktree::
 -      Set the path to the root of the work tree.
 +      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. It can be
 -      an absolute path or a relative path to the .git directory,
 -      either specified by --git-dir or GIT_DIR, or automatically
 -      discovered.
 -      If --git-dir or GIT_DIR are specified but none of
 +      variable and the '--work-tree' command line option.
 +      The value can an absolute path or relative to the path to
 +      the .git directory, which is either specified by --git-dir
 +      or GIT_DIR, or automatically discovered.
 +      If --git-dir or GIT_DIR is specified but none of
        --work-tree, GIT_WORK_TREE and core.worktree is specified,
 -      the current working directory is regarded as the root of the
 -      work tree.
 +      the current working directory is regarded as the top level
 +      of your working tree.
  +
  Note that this variable is honored even when set in a configuration
 -file in a ".git" subdirectory of a directory, and its value differs
 +file in a ".git" subdirectory of a directory and its value differs
  from the latter directory (e.g. "/path/to/.git/config" has
  core.worktree set to "/different/path"), which is most likely a
 -misconfiguration.  Running git commands in "/path/to" directory will
 +misconfiguration.  Running git commands in the "/path/to" directory will
  still use "/different/path" as the root of the work tree and can cause
 -great confusion to the users.
 +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).
  
  core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
@@@ -376,15 -374,6 +376,15 @@@ core.warnAmbiguousRefs:
        If true, git will warn you if the ref name you passed it is ambiguous
        and might match multiple refs in the .git/refs/ tree. True by default.
  
 +core.abbrevguard::
 +      Even though git makes sure that it uses enough hexdigits to show
 +      an abbreviated object name unambiguously, as more objects are
 +      added to the repository over time, a short name that used to be
 +      unique will stop being unique.  Git uses this many extra hexdigits
 +      that are more than necessary to make the object name currently
 +      unique, in the hope that its output will stay unique a bit longer.
 +      Defaults to 0.
 +
  core.compression::
        An integer -1..9, indicating a default compression level.
        -1 is the zlib default. 0 means no compression,
@@@ -461,21 -450,13 +461,28 @@@ core.excludesfile:
        to the value of `$HOME` and "{tilde}user/" to the specified user's
        home directory.  See linkgit:gitignore[5].
  
 +core.askpass::
 +      Some commands (e.g. svn and http interfaces) that interactively
 +      ask for a password can be told to use an external program given
 +      via the value of this variable. Can be overridden by the 'GIT_ASKPASS'
 +      environment variable. If not set, fall back to the value of the
 +      'SSH_ASKPASS' environment variable or, failing that, a simple password
 +      prompt. The external program shall be given a suitable prompt as
 +      command line argument and write the password on its STDOUT.
 +
 +core.attributesfile::
 +      In addition to '.gitattributes' (per-directory) and
 +      '.git/info/attributes', git looks into this file for attributes
 +      (see linkgit:gitattributes[5]). Path expansions are made the same
 +      way as for `core.excludesfile`.
 +
+ core.ignoredareprecious::
+       By default ignored (i.e. trashable) untracked files are
+       automatically removed from the working tree when they get in
+       the way of merge or switching between branches.  This option
+       declares that such files are precious instead, and prevents
+       merges and checkouts from succeeding.
  core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
@@@ -524,9 -505,6 +531,9 @@@ core.whitespace:
    part of the line terminator, i.e. with it, `trailing-space`
    does not trigger if the character before such a carriage-return
    is not a whitespace (not enabled by default).
 +* `tabwidth=<n>` tells how many character positions a tab occupies; this
 +  is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`
 +  errors. The default tab width is 8. Allowed values are 1 to 63.
  
  core.fsyncobjectfiles::
        This boolean will enable 'fsync()' when writing object files.
@@@ -568,13 -546,9 +575,13 @@@ core.sparseCheckout:
        linkgit:git-read-tree[1] for more information.
  
  add.ignore-errors::
 +add.ignoreErrors::
        Tells 'git add' to continue adding files when some files cannot be
        added due to indexing errors. Equivalent to the '--ignore-errors'
 -      option of linkgit:git-add[1].
 +      option of linkgit:git-add[1].  Older versions of git accept only
 +      `add.ignore-errors`, which does not follow the usual naming
 +      convention for configuration variables.  Newer versions of git
 +      honor `add.ignoreErrors` as well.
  
  alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
@@@ -619,9 -593,8 +626,9 @@@ branch.autosetupmerge:
        this behavior can be chosen per-branch using the `--track`
        and `--no-track` options. The valid settings are: `false` -- no
        automatic setup is done; `true` -- automatic setup is done when the
 -      starting point is a remote branch; `always` -- automatic setup is
 -      done when the starting point is either a local branch or remote
 +      starting point is a remote-tracking branch; `always` --
 +      automatic setup is done when the starting point is either a
 +      local branch or remote-tracking
        branch. This option defaults to true.
  
  branch.autosetuprebase::
        When `local`, rebase is set to true for tracked branches of
        other local branches.
        When `remote`, rebase is set to true for tracked branches of
 -      remote branches.
 +      remote-tracking branches.
        When `always`, rebase will be set to true for all tracking
        branches.
        See "branch.autosetupmerge" for details on how to set up a
@@@ -699,7 -672,7 +706,7 @@@ color.branch:
  color.branch.<slot>::
        Use customized color for branch coloration. `<slot>` is one of
        `current` (the current branch), `local` (a local branch),
 -      `remote` (a tracking branch in refs/remotes/), `plain` (other
 +      `remote` (a remote-tracking branch in refs/remotes/), `plain` (other
        refs).
  +
  The value for these configuration variables is a list of colors (at most
@@@ -727,7 -700,7 +734,7 @@@ color.diff.<slot>:
  color.decorate.<slot>::
        Use customized color for 'git log --decorate' output.  `<slot>` is one
        of `branch`, `remoteBranch`, `tag`, `stash` or `HEAD` for local
 -      branches, remote tracking branches, tags, stash and HEAD, respectively.
 +      branches, remote-tracking branches, tags, stash and HEAD, respectively.
  
  color.grep::
        When set to `always`, always highlight matches.  When `false` (or
@@@ -792,8 -765,7 +799,8 @@@ color.status.<slot>:
        one of `header` (the header text of the status message),
        `added` or `updated` (files which are added but not committed),
        `changed` (files which are changed but not added in the index),
 -      `untracked` (files which are not tracked by git), or
 +      `untracked` (files which are not tracked by git),
 +      `branch` (the current branch), or
        `nobranch` (the color the 'no branch' warning is shown in, defaulting
        to red). The values of these variables may be specified as in
        color.branch.<slot>.
@@@ -839,6 -811,8 +846,6 @@@ diff.mnemonicprefix:
        standard "a/" and "b/" depending on what is being compared.  When
        this configuration is in effect, reverse diff output also swaps
        the order of the prefixes:
 -diff.noprefix::
 -      If set, 'git diff' does not show any source or destination prefix.
  `git diff`;;
        compares the (i)ndex and the (w)ork tree;
  `git diff HEAD`;;
  `git diff --no-index a b`;;
        compares two non-git things (1) and (2).
  
 +diff.noprefix::
 +      If set, 'git diff' does not show any source or destination prefix.
 +
  diff.renameLimit::
        The number of files to consider when performing the copy/rename
        detection; equivalent to the 'git diff' option '-l'.
@@@ -865,8 -836,7 +872,8 @@@ diff.renames:
  diff.ignoreSubmodules::
        Sets the default value of --ignore-submodules. Note that this
        affects only 'git diff' Porcelain, and not lower level 'diff'
 -      commands such as 'git diff-files'.
 +      commands such as 'git diff-files'. 'git checkout' also honors
 +      this setting when reporting uncommitted changes.
  
  diff.suppressBlankEmpty::
        A boolean to inhibit the standard behavior of printing a space
@@@ -899,11 -869,6 +906,11 @@@ diff.wordRegex:
        sequences that match the regular expression are "words", all other
        characters are *ignorable* whitespace.
  
 +fetch.recurseSubmodules::
 +      A boolean value which changes the behavior for fetch and pull, the
 +      default is to not recursively fetch populated submodules unless
 +      configured otherwise.
 +
  fetch.unpackLimit::
        If the number of objects fetched over the git native
        transfer is below this
@@@ -998,7 -963,7 +1005,7 @@@ gc.packrefs:
        Running `git pack-refs` in a repository renders it
        unclonable by Git versions prior to 1.5.1.2 over dumb
        transports such as HTTP.  This variable determines whether
 -      'git gc' runs `git pack-refs`. This can be set to `nobare`
 +      'git gc' runs `git pack-refs`. This can be set to `notbare`
        to enable it within all non-bare repos or it can be set to a
        boolean value.  The default is `true`.
  
@@@ -1127,7 -1092,7 +1134,7 @@@ gui.newbranchtemplate:
        linkgit:git-gui[1].
  
  gui.pruneduringfetch::
 -      "true" if linkgit:git-gui[1] should prune tracking branches when
 +      "true" if linkgit:git-gui[1] should prune remote-tracking branches when
        performing a fetch. The default value is "false".
  
  gui.trustmtime::
@@@ -1290,15 -1255,6 +1297,15 @@@ http.noEPSV:
        support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
        environment variable. Default is false (curl will use EPSV).
  
 +http.useragent::
 +      The HTTP USER_AGENT string presented to an HTTP server.  The default
 +      value represents the version of the client git such as git/1.7.1.
 +      This option allows you to override this value to a more common value
 +      such as Mozilla/4.0.  This may be necessary, for instance, if
 +      connecting through a firewall that restricts HTTP connections to a set
 +      of common USER_AGENT strings (but not including those like git/1.7.1).
 +      Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
 +
  i18n.commitEncoding::
        Character encoding the commit messages are stored in; git itself
        does not care per se, but this information is necessary e.g. when
@@@ -1347,11 -1303,10 +1354,11 @@@ interactive.singlekey:
        ignored if portable keystroke input is not available.
  
  log.date::
 -      Set default date-time mode for the log command. Setting log.date
 -      value is similar to using 'git log'\'s --date option. The value is one of the
 -      following alternatives: {relative,local,default,iso,rfc,short}.
 -      See linkgit:git-log[1].
 +      Set the default date-time mode for the 'log' command.
 +      Setting a value for log.date is similar to using 'git log''s
 +      `\--date` option.  Possible values are `relative`, `local`,
 +      `default`, `iso`, `rfc`, and `short`; see linkgit:git-log[1]
 +      for details.
  
  log.decorate::
        Print out the ref names of any commits that are shown by the log
@@@ -1497,10 -1452,6 +1504,10 @@@ pack.compression:
        not set,  defaults to -1, the zlib default, which is "a default
        compromise between speed and compression (currently equivalent
        to level 6)."
 ++
 +Note that changing the compression level will not automatically recompress
 +all existing objects. You can force recompression by passing the -F option
 +to linkgit:git-repack[1].
  
  pack.deltaCacheSize::
        The maximum memory in bytes used for caching deltas in
@@@ -1556,13 -1507,11 +1563,13 @@@ pack.packSizeLimit:
        supported.
  
  pager.<cmd>::
 -      Allows turning on or off pagination of the output of a
 -      particular git subcommand when writing to a tty.  If
 -      `\--paginate` or `\--no-pager` is specified on the command line,
 -      it takes precedence over this option.  To disable pagination for
 -      all commands, set `core.pager` or `GIT_PAGER` to `cat`.
 +      If the value is boolean, turns on or off pagination of the
 +      output of a particular git subcommand when writing to a tty.
 +      Otherwise, turns on pagination for the subcommand using the
 +      pager specified by the value of `pager.<cmd>`.  If `\--paginate`
 +      or `\--no-pager` is specified on the command line, it takes
 +      precedence over this option.  To disable pagination for all
 +      commands, set `core.pager` or `GIT_PAGER` to `cat`.
  
  pretty.<name>::
        Alias for a --pretty= format string, as specified in
@@@ -1587,20 -1536,17 +1594,20 @@@ push.default:
        no refspec is implied by any of the options given on the command
        line. Possible values are:
  +
 -* `nothing` do not push anything.
 -* `matching` push all matching branches.
 +* `nothing` do not push anything.
 +* `matching` push all matching branches.
    All branches having the same name in both ends are considered to be
    matching. This is the default.
 -* `tracking` push the current branch to its upstream branch.
 -* `current` push the current branch to a branch of the same name.
 +* `tracking` push the current branch to its upstream branch.
 +* `current` push the current branch to a branch of the same name.
  
  rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
  
 +rebase.autosquash::
 +      If set to true enable '--autosquash' option by default.
 +
  receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
        receiving data from git-push and updating refs.  You can stop
@@@ -1695,9 -1641,7 +1702,9 @@@ remote.<name>.tagopt:
        Setting this value to \--no-tags disables automatic tag following when
        fetching from remote <name>. Setting it to \--tags will fetch every
        tag from remote <name>, even if they are not reachable from remote
 -      branch heads.
 +      branch heads. Passing these flags directly to linkgit:git-fetch[1] can
 +      override this setting. See options \--tags and \--no-tags of
 +      linkgit:git-fetch[1].
  
  remote.<name>.vcs::
        Setting this to a value <vcs> will cause git to interact with
@@@ -1764,7 -1708,6 +1771,7 @@@ sendemail.to:
  sendemail.smtpdomain::
  sendemail.smtpserver::
  sendemail.smtpserverport::
 +sendemail.smtpserveroption::
  sendemail.smtpuser::
  sendemail.thread::
  sendemail.validate::
@@@ -1793,9 -1736,9 +1800,9 @@@ status.showUntrackedFiles:
        the untracked files. Possible values are:
  +
  --
 -      - 'no'     - Show no untracked files
 -      - 'normal' - Shows untracked files and directories
 -      - 'all'    - Shows also individual files in untracked directories.
 +* `no` - Show no untracked files.
 +* `normal` - Show untracked files and directories.
 +* `all` - Show also individual files in untracked directories.
  --
  +
  If this variable is not specified, it defaults to 'normal'.
@@@ -1818,13 -1761,6 +1825,13 @@@ submodule.<name>.update:
        URL and other values found in the `.gitmodules` file.  See
        linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
  
 +submodule.<name>.fetchRecurseSubmodules::
 +      This option can be used to enable/disable recursive fetching of this
 +      submodule. It can be overridden by using the --[no-]recurse-submodules
 +      command line option to "git fetch" and "git pull".
 +      This setting will override that from in the linkgit:gitmodules[5]
 +      file.
 +
  submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. When set to "all", it will never be considered
        Using "none" (the default when this option is not set) also shows
        submodules that have untracked files in their work tree as changed.
        This setting overrides any setting made in .gitmodules for this submodule,
 -      both settings can be overriden on the command line by using the
 +      both settings can be overridden on the command line by using the
        "--ignore-submodules" option.
  
  tar.umask::
diff --combined builtin/checkout.c
index d3cfaf0f45cea2c27dd8d7caf47ac43cd499c79e,cb83e6a6318b8b723a3ab9d97eac1556e2aa1d9b..0ca8f6a2061b177410136fb89b988806a05eb16e
@@@ -18,7 -18,6 +18,7 @@@
  #include "xdiff-interface.h"
  #include "ll-merge.h"
  #include "resolve-undo.h"
 +#include "submodule.h"
  
  static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@@ -41,7 -40,6 +41,7 @@@ struct checkout_opts 
        const char *new_orphan_branch;
        int new_branch_log;
        enum branch_track track;
 +      struct diff_options diff_options;
  };
  
  static int post_checkout_hook(struct commit *old, struct commit *new,
@@@ -156,12 -154,8 +156,12 @@@ static int checkout_merged(int pos, str
        read_mmblob(&ours, active_cache[pos+1]->sha1);
        read_mmblob(&theirs, active_cache[pos+2]->sha1);
  
 +      /*
 +       * NEEDSWORK: re-create conflicts from merges with
 +       * merge.renormalize set, too
 +       */
        status = ll_merge(&result_buf, path, &ancestor, "base",
 -                        &ours, "ours", &theirs, "theirs", 0);
 +                        &ours, "ours", &theirs, "theirs", NULL);
        free(ancestor.ptr);
        free(ours.ptr);
        free(theirs.ptr);
@@@ -284,12 -278,11 +284,12 @@@ static int checkout_paths(struct tree *
        return errs;
  }
  
 -static void show_local_changes(struct object *head)
 +static void show_local_changes(struct object *head, struct diff_options *opts)
  {
        struct rev_info rev;
        /* I think we want full paths, even if we're in a subdirectory. */
        init_revisions(&rev, NULL);
 +      rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
        if (diff_setup_done(&rev.diffopt) < 0)
                die("diff_setup_done failed");
@@@ -383,7 -376,7 +383,7 @@@ static int merge_working_tree(struct ch
                topts.src_index = &the_index;
                topts.dst_index = &the_index;
  
 -              topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
 +              setup_unpack_trees_porcelain(&topts, "checkout");
  
                refresh_cache(REFRESH_QUIET);
  
                topts.gently = opts->merge && old->commit;
                topts.verbose_update = !opts->quiet;
                topts.fn = twoway_merge;
-               topts.dir = xcalloc(1, sizeof(*topts.dir));
-               topts.dir->flags |= DIR_SHOW_IGNORED;
-               topts.dir->exclude_per_dir = ".gitignore";
+               if (!ignored_are_precious) {
+                       topts.dir = xcalloc(1, sizeof(*topts.dir));
+                       topts.dir->flags |= DIR_SHOW_IGNORED;
+                       topts.dir->exclude_per_dir = ".gitignore";
+               }
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           (unsigned char *)EMPTY_TREE_SHA1_BIN);
                         */
  
                        add_files_to_cache(NULL, NULL, 0);
 +                      /*
 +                       * NEEDSWORK: carrying over local changes
 +                       * when branches have different end-of-line
 +                       * normalization (or clean+smudge rules) is
 +                       * a pain; plumb in an option to set
 +                       * o.renormalize?
 +                       */
                        init_merge_options(&o);
                        o.verbosity = 0;
                        work = write_tree_from_memory(&o);
                die("unable to write new index file");
  
        if (!opts->force && !opts->quiet)
 -              show_local_changes(&new->commit->object);
 +              show_local_changes(&new->commit->object, &opts->diff_options);
  
        return 0;
  }
@@@ -621,16 -609,7 +623,16 @@@ static int switch_branches(struct check
  
  static int git_checkout_config(const char *var, const char *value, void *cb)
  {
 -      return git_xmerge_config(var, value, cb);
 +      if (!strcmp(var, "diff.ignoresubmodules")) {
 +              struct checkout_opts *opts = cb;
 +              handle_ignore_submodules_arg(&opts->diff_options, value);
 +              return 0;
 +      }
 +
 +      if (!prefixcmp(var, "submodule."))
 +              return parse_submodule_config_option(var, value);
 +
 +      return git_xmerge_config(var, value, NULL);
  }
  
  static int interactive_checkout(const char *revision, const char **pathspec,
@@@ -678,7 -657,7 +680,7 @@@ static const char *unique_tracking_name
  int cmd_checkout(int argc, const char **argv, const char *prefix)
  {
        struct checkout_opts opts;
 -      unsigned char rev[20];
 +      unsigned char rev[20], branch_rev[20];
        const char *arg;
        struct branch_info new;
        struct tree *source_tree = NULL;
        int patch_mode = 0;
        int dwim_new_local_branch = 1;
        struct option options[] = {
 -              OPT__QUIET(&opts.quiet),
 +              OPT__QUIET(&opts.quiet, "suppress progress reporting"),
                OPT_STRING('b', NULL, &opts.new_branch, "branch",
                           "create and checkout a new branch"),
                OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
                           "create/reset and checkout a branch"),
 -              OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
 -              OPT_SET_INT('t', "track",  &opts.track, "track",
 +              OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
 +              OPT_SET_INT('t', "track",  &opts.track, "set upstream info for new branch",
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
 -              OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
 +              OPT_SET_INT('2', "ours", &opts.writeout_stage, "checkout our version for unmerged files",
                            2),
 -              OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
 +              OPT_SET_INT('3', "theirs", &opts.writeout_stage, "checkout their version for unmerged files",
                            3),
 -              OPT_BOOLEAN('f', "force", &opts.force, "force"),
 +              OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
+               OPT_BOOLEAN('i', "ignored-are-precious", &ignored_are_precious,
+                 "fail when an ignored file needs to be overwritten"),
 -              OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
 +              OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
                OPT_STRING(0, "conflict", &conflict_style, "style",
                           "conflict style (merge or diff3)"),
                OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
  
 -      git_config(git_checkout_config, NULL);
 +      gitmodules_config();
 +      git_config(git_checkout_config, &opts);
  
        opts.track = BRANCH_TRACK_UNSPECIFIED;
  
         *   between A and B, A...B names that merge base.
         *
         *   With no paths, if <something> is _not_ a commit, no -t nor -b
 -       *   was given, and there is a tracking branch whose name is
 +       *   was given, and there is a remote-tracking branch whose name is
         *   <something> in one and only one remote, then this is a short-hand
 -       *   to fork local <something> from that remote tracking branch.
 +       *   to fork local <something> from that remote-tracking branch.
         *
         *   Otherwise <something> shall not be ambiguous.
         *   - If it's *only* a reference, treat it like case (1).
                argc--;
  
                new.name = arg;
 -              if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
 -                      setup_branch_path(&new);
 +              setup_branch_path(&new);
  
 -                      if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) &&
 -                          resolve_ref(new.path, rev, 1, NULL))
 -                              ;
 -                      else
 -                              new.path = NULL;
 +              if (check_ref_format(new.path) == CHECK_REF_FORMAT_OK &&
 +                  resolve_ref(new.path, branch_rev, 1, NULL))
 +                      hashcpy(rev, branch_rev);
 +              else
 +                      new.path = NULL; /* not an existing branch */
 +
 +              if (!(new.commit = lookup_commit_reference_gently(rev, 1))) {
 +                      /* not a commit */
 +                      source_tree = parse_tree_indirect(rev);
 +              } else {
                        parse_commit(new.commit);
                        source_tree = new.commit->tree;
 -              } else
 -                      source_tree = parse_tree_indirect(rev);
 +              }
  
                if (!source_tree)                   /* case (1): want a tree */
                        die("reference is not a tree: %s", arg);
diff --combined builtin/merge.c
index 9403747b6afeb89a9079e7203f5a027014411495,9ca7c5c726aa381a980c5a52593d8277717c13bb..b573d9804a4a20b7a05e56db6f81ce6bf0f16cf3
@@@ -42,7 -42,7 +42,7 @@@ static const char * const builtin_merge
        NULL
  };
  
 -static int show_diffstat = 1, option_log, squash;
 +static int show_diffstat = 1, shortlog_len, squash;
  static int option_commit = 1, allow_fast_forward = 1;
  static int fast_forward_only;
  static int allow_trivial = 1, have_message;
@@@ -54,10 -54,8 +54,10 @@@ static size_t use_strategies_nr, use_st
  static const char **xopts;
  static size_t xopts_nr, xopts_alloc;
  static const char *branch;
 +static int option_renormalize;
  static int verbosity;
  static int allow_rerere_auto;
 +static int abort_current_merge;
  
  static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@@ -133,7 -131,6 +133,7 @@@ static struct strategy *get_strategy(co
  
        ret = xcalloc(1, sizeof(struct strategy));
        ret->name = xstrdup(name);
 +      ret->attr = NO_TRIVIAL;
        return ret;
  }
  
@@@ -178,9 -175,8 +178,9 @@@ static struct option builtin_merge_opti
        OPT_BOOLEAN(0, "stat", &show_diffstat,
                "show a diffstat at the end of the merge"),
        OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
 -      OPT_BOOLEAN(0, "log", &option_log,
 -              "add list of one-line log to merge commit message"),
 +      { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
 +        "add (at most <n>) entries from shortlog to merge commit message",
 +        PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
        OPT_BOOLEAN(0, "squash", &squash,
                "create a single commit instead of doing a merge"),
        OPT_BOOLEAN(0, "commit", &option_commit,
                "allow fast-forward (default)"),
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
                "abort if fast-forward is not possible"),
+       OPT_BOOLEAN('i', "ignored-are-precious", &ignored_are_precious,
+         "fail when an ignored file needs to be overwritten"),
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
                "merge strategy to use", option_parse_strategy),
                "message to be used for the merge commit (if any)",
                option_parse_message),
        OPT__VERBOSITY(&verbosity),
 +      OPT_BOOLEAN(0, "abort", &abort_current_merge,
 +              "abort the current in-progress merge"),
        OPT_END()
  };
  
@@@ -237,24 -233,6 +239,24 @@@ static void save_state(void
                die("not a valid object: %s", buffer.buf);
  }
  
 +static void read_empty(unsigned const char *sha1, int verbose)
 +{
 +      int i = 0;
 +      const char *args[7];
 +
 +      args[i++] = "read-tree";
 +      if (verbose)
 +              args[i++] = "-v";
 +      args[i++] = "-m";
 +      args[i++] = "-u";
 +      args[i++] = EMPTY_TREE_SHA1_HEX;
 +      args[i++] = sha1_to_hex(sha1);
 +      args[i] = NULL;
 +
 +      if (run_command_v_opt(args, RUN_GIT_CMD))
 +              die("read-tree failed");
 +}
 +
  static void reset_hard(unsigned const char *sha1, int verbose)
  {
        int i = 0;
@@@ -424,7 -402,7 +426,7 @@@ static void merge_name(const char *remo
                        goto cleanup;
                }
                if (!prefixcmp(found_ref, "refs/remotes/")) {
 -                      strbuf_addf(msg, "%s\t\tremote branch '%s' of .\n",
 +                      strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
                strbuf_addstr(&truname, "refs/heads/");
                strbuf_addstr(&truname, remote);
                strbuf_setlen(&truname, truname.len - len);
 -              if (resolve_ref(truname.buf, buf_sha, 0, NULL)) {
 +              if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
                                    sha1_to_hex(remote_head->sha1),
@@@ -510,8 -488,7 +512,8 @@@ static int git_merge_config(const char 
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
                if (argc < 0)
 -                      die("Bad branch.%s.mergeoptions string", branch);
 +                      die("Bad branch.%s.mergeoptions string: %s", branch,
 +                          split_cmdline_strerror(argc));
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
                return git_config_string(&pull_octopus, k, v);
 -      else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
 -              option_log = git_config_bool(k, v);
 +      else if (!strcmp(k, "merge.renormalize"))
 +              option_renormalize = git_config_bool(k, v);
 +      else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
 +              int is_bool;
 +              shortlog_len = git_config_bool_or_int(k, v, &is_bool);
 +              if (!is_bool && shortlog_len < 0)
 +                      return error("%s: negative length %s", k, v);
 +              if (is_bool && shortlog_len)
 +                      shortlog_len = DEFAULT_MERGE_LOG_LEN;
 +              return 0;
 +      }
        return git_diff_ui_config(k, v, cb);
  }
  
@@@ -582,8 -550,7 +584,8 @@@ static void write_tree_trivial(unsigne
                die("git write-tree failed to write a tree");
  }
  
 -int try_merge_command(const char *strategy, struct commit_list *common,
 +int try_merge_command(const char *strategy, size_t xopts_nr,
 +                    const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
  {
        const char **args;
@@@ -659,11 -626,18 +661,11 @@@ static int try_merge_strategy(const cha
                if (!strcmp(strategy, "subtree"))
                        o.subtree_shift = "";
  
 -              for (x = 0; x < xopts_nr; x++) {
 -                      if (!strcmp(xopts[x], "ours"))
 -                              o.recursive_variant = MERGE_RECURSIVE_OURS;
 -                      else if (!strcmp(xopts[x], "theirs"))
 -                              o.recursive_variant = MERGE_RECURSIVE_THEIRS;
 -                      else if (!strcmp(xopts[x], "subtree"))
 -                              o.subtree_shift = "";
 -                      else if (!prefixcmp(xopts[x], "subtree="))
 -                              o.subtree_shift = xopts[x]+8;
 -                      else
 +              o.renormalize = option_renormalize;
 +
 +              for (x = 0; x < xopts_nr; x++)
 +                      if (parse_merge_opt(&o, xopts[x]))
                                die("Unknown option for merge-recursive: -X%s", xopts[x]);
 -              }
  
                o.branch1 = head_arg;
                o.branch2 = remoteheads->item->util;
                rollback_lock_file(lock);
                return clean ? 0 : 1;
        } else {
 -              return try_merge_command(strategy, common, head_arg, remoteheads);
 +              return try_merge_command(strategy, xopts_nr, xopts,
 +                                              common, head_arg, remoteheads);
        }
  }
  
@@@ -722,9 -695,11 +724,11 @@@ int checkout_fast_forward(const unsigne
        memset(&opts, 0, sizeof(opts));
        memset(&t, 0, sizeof(t));
        memset(&dir, 0, sizeof(dir));
-       dir.flags |= DIR_SHOW_IGNORED;
-       dir.exclude_per_dir = ".gitignore";
-       opts.dir = &dir;
+       if (!ignored_are_precious) {
+               dir.flags |= DIR_SHOW_IGNORED;
+               dir.exclude_per_dir = ".gitignore";
+               opts.dir = &dir;
+       }
  
        opts.head_idx = 1;
        opts.src_index = &the_index;
        opts.verbose_update = 1;
        opts.merge = 1;
        opts.fn = twoway_merge;
 -      opts.msgs = get_porcelain_error_msgs();
 +      setup_unpack_trees_porcelain(&opts, "merge");
  
        trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
@@@ -845,7 -820,7 +849,7 @@@ static int finish_automerge(struct comm
        return 0;
  }
  
 -static int suggest_conflicts(void)
 +static int suggest_conflicts(int renormalizing)
  {
        FILE *fp;
        int pos;
@@@ -924,9 -899,22 +928,9 @@@ int cmd_merge(int argc, const char **ar
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list **remotes = &remoteheads;
  
 -      if (read_cache_unmerged()) {
 -              die_resolve_conflict("merge");
 -      }
 -      if (file_exists(git_path("MERGE_HEAD"))) {
 -              /*
 -               * There is no unmerged entry, don't advise 'git
 -               * add/rm <file>', just 'git commit'.
 -               */
 -              if (advice_resolve_conflict)
 -                      die("You have not concluded your merge (MERGE_HEAD exists).\n"
 -                          "Please, commit your changes before you can merge.");
 -              else
 -                      die("You have not concluded your merge (MERGE_HEAD exists).");
 -      }
 +      if (argc == 2 && !strcmp(argv[1], "-h"))
 +              usage_with_options(builtin_merge_usage, builtin_merge_options);
  
 -      resolve_undo_clear();
        /*
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
  
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
                        builtin_merge_usage, 0);
 +
 +      if (abort_current_merge) {
 +              int nargc = 2;
 +              const char *nargv[] = {"reset", "--merge", NULL};
 +
 +              if (!file_exists(git_path("MERGE_HEAD")))
 +                      die("There is no merge to abort (MERGE_HEAD missing).");
 +
 +              /* Invoke 'git reset --merge' */
 +              return cmd_reset(nargc, nargv, prefix);
 +      }
 +
 +      if (read_cache_unmerged())
 +              die_resolve_conflict("merge");
 +
 +      if (file_exists(git_path("MERGE_HEAD"))) {
 +              /*
 +               * There is no unmerged entry, don't advise 'git
 +               * add/rm <file>', just 'git commit'.
 +               */
 +              if (advice_resolve_conflict)
 +                      die("You have not concluded your merge (MERGE_HEAD exists).\n"
 +                          "Please, commit your changes before you can merge.");
 +              else
 +                      die("You have not concluded your merge (MERGE_HEAD exists).");
 +      }
 +      resolve_undo_clear();
 +
        if (verbosity < 0)
                show_diffstat = 0;
  
                        die("%s - not something we can merge", argv[0]);
                update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
                                DIE_ON_ERR);
 -              reset_hard(remote_head->sha1, 0);
 +              read_empty(remote_head->sha1, 0);
                return 0;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &merge_names);
  
 -              if (have_message && option_log)
 -                      fmt_merge_msg_shortlog(&merge_names, &merge_msg);
 -              else if (!have_message)
 -                      fmt_merge_msg(option_log, &merge_names, &merge_msg);
 -
 -
 -              if (!(have_message && !option_log) && merge_msg.len)
 -                      strbuf_setlen(&merge_msg, merge_msg.len-1);
 +              if (!have_message || shortlog_len) {
 +                      fmt_merge_msg(&merge_names, &merge_msg, !have_message,
 +                                    shortlog_len);
 +                      if (merge_msg.len)
 +                              strbuf_setlen(&merge_msg, merge_msg.len - 1);
 +              }
        }
  
        if (head_invalid || !argc)
                        "stopped before committing as requested\n");
                return 0;
        } else
 -              return suggest_conflicts();
 +              return suggest_conflicts(option_renormalize);
  }
diff --combined cache.h
index 9ca16ae49585198fb233cdb9feb5f59161d6c505,cf5516faf0f9e755b38678cc17741f1d186859aa..4b9268dfa022fe0a03f1cd8b1b0b158ab4616f5d
+++ b/cache.h
@@@ -170,26 -170,27 +170,26 @@@ struct cache_entry 
   *
   * In-memory only flags
   */
 -#define CE_UPDATE    (0x10000)
 -#define CE_REMOVE    (0x20000)
 -#define CE_UPTODATE  (0x40000)
 -#define CE_ADDED     (0x80000)
 +#define CE_UPDATE            (1 << 16)
 +#define CE_REMOVE            (1 << 17)
 +#define CE_UPTODATE          (1 << 18)
 +#define CE_ADDED             (1 << 19)
  
 -#define CE_HASHED    (0x100000)
 -#define CE_UNHASHED  (0x200000)
 -#define CE_CONFLICTED (0x800000)
 +#define CE_HASHED            (1 << 20)
 +#define CE_UNHASHED          (1 << 21)
 +#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
 +#define CE_CONFLICTED        (1 << 23)
  
 -/* Only remove in work directory, not index */
 -#define CE_WT_REMOVE (0x400000)
 -
 -#define CE_UNPACKED  (0x1000000)
 +#define CE_UNPACKED          (1 << 24)
 +#define CE_NEW_SKIP_WORKTREE (1 << 25)
  
  /*
   * Extended on-disk flags
   */
 -#define CE_INTENT_TO_ADD 0x20000000
 -#define CE_SKIP_WORKTREE 0x40000000
 +#define CE_INTENT_TO_ADD     (1 << 29)
 +#define CE_SKIP_WORKTREE     (1 << 30)
  /* CE_EXTENDED2 is for future extension */
 -#define CE_EXTENDED2 0x80000000
 +#define CE_EXTENDED2         (1 << 31)
  
  #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
@@@ -277,16 -278,9 +277,16 @@@ static inline int ce_to_dtype(const str
        else
                return DT_UNKNOWN;
  }
 -#define canon_mode(mode) \
 -      (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
 -      S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
 +static inline unsigned int canon_mode(unsigned int mode)
 +{
 +      if (S_ISREG(mode))
 +              return S_IFREG | ce_permissions(mode);
 +      if (S_ISLNK(mode))
 +              return S_IFLNK;
 +      if (S_ISDIR(mode))
 +              return S_IFDIR;
 +      return S_IFGITLINK;
 +}
  
  #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) flexible_size(cache_entry,len)
@@@ -385,7 -379,6 +385,7 @@@ static inline enum object_type object_t
  #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
  #define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
  #define CONFIG_ENVIRONMENT "GIT_CONFIG"
 +#define CONFIG_DATA_ENVIRONMENT "GIT_CONFIG_PARAMETERS"
  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
  #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
  #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
   * environment creation or simple walk of the list.
   * The number of non-NULL entries is available as a macro.
   */
 -#define LOCAL_REPO_ENV_SIZE 8
 +#define LOCAL_REPO_ENV_SIZE 9
  extern const char *const local_repo_env[LOCAL_REPO_ENV_SIZE + 1];
  
  extern int is_bare_repository_cfg;
@@@ -428,7 -421,7 +428,7 @@@ extern const char **get_pathspec(const 
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
 -extern const char *prefix_path(const char *prefix, int len, const char *path);
 +extern char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix, const char *name);
@@@ -445,7 -438,7 +445,7 @@@ extern int init_db(const char *template
   * at least 'nr' entries; the number of entries currently allocated
   * is 'alloc', using the standard growing factor alloc_nr() macro.
   *
 - * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
 + * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
   */
  #define ALLOC_GROW(x, nr, alloc) \
        do { \
@@@ -500,22 -493,7 +500,22 @@@ extern int index_name_is_other(const st
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
 -extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 +struct pathspec {
 +      const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
 +      int nr;
 +      int has_wildcard:1;
 +      int recursive:1;
 +      int max_depth;
 +      struct pathspec_item {
 +              const char *match;
 +              int len;
 +              int has_wildcard:1;
 +      } *items;
 +};
 +
 +extern int init_pathspec(struct pathspec *, const char **);
 +extern void free_pathspec(struct pathspec *);
 +extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *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_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);
@@@ -560,7 -538,6 +560,7 @@@ extern int assume_unchanged
  extern int prefer_symlink_refs;
  extern int log_all_ref_updates;
  extern int warn_ambiguous_refs;
 +extern int unique_abbrev_extra_length;
  extern int shared_repository;
  extern const char *apply_default_whitespace;
  extern const char *apply_default_ignorewhitespace;
@@@ -571,10 -548,10 +571,11 @@@ extern size_t packed_git_window_size
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
  extern int read_replace_refs;
+ extern int ignored_are_precious;
  extern int fsync_object_files;
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
 +extern int core_clock_skew;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -665,9 -642,6 +666,9 @@@ extern char *git_pathdup(const char *fm
  /* Return a statically allocated filename matching the sha1 signature */
  extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +extern char *git_path_submodule(const char *path, const char *fmt, ...)
 +      __attribute__((format (printf, 2, 3)));
 +
  extern char *sha1_file_name(const unsigned char *sha1);
  extern char *sha1_pack_name(const unsigned char *sha1);
  extern char *sha1_pack_index_name(const unsigned char *sha1);
@@@ -876,7 -850,7 +877,7 @@@ struct cache_def 
  
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 -extern int has_symlink_or_noent_leading_path(const char *name, int len);
 +extern int check_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
@@@ -998,12 -972,9 +999,12 @@@ extern int update_server_info(int)
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
 +extern void git_config_push_parameter(const char *text);
  extern int git_config_parse_parameter(const char *text);
 +extern int git_config_parse_environment(void);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
 +extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  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 *);
@@@ -1021,9 -992,6 +1022,9 @@@ extern int git_env_bool(const char *, i
  extern int git_config_system(void);
  extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
 +extern const char *get_log_output_encoding(void);
 +extern const char *get_commit_output_encoding(void);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1063,7 -1031,6 +1064,7 @@@ extern int pager_in_use(void)
  extern int pager_use_color;
  
  extern const char *editor_program;
 +extern const char *askpass_program;
  extern const char *excludes_file;
  
  /* base85 */
@@@ -1083,14 -1050,12 +1084,14 @@@ __attribute__((format (printf, 1, 2))
  extern void trace_printf(const char *format, ...);
  __attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
 +extern void trace_repo_setup(const char *prefix);
  
  /* convert.c */
  /* returns 1 if *dst was used */
  extern int convert_to_git(const char *path, const char *src, size_t len,
                            struct strbuf *dst, enum safe_crlf checksafe);
  extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 +extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
  
  /* add */
  /*
@@@ -1109,17 -1074,15 +1110,17 @@@ void shift_tree_by(const unsigned char 
  /*
   * whitespace rules.
   * used by both diff and apply
 + * last two digits are tab width
   */
 -#define WS_BLANK_AT_EOL         01
 -#define WS_SPACE_BEFORE_TAB   02
 -#define WS_INDENT_WITH_NON_TAB        04
 -#define WS_CR_AT_EOL           010
 -#define WS_BLANK_AT_EOF        020
 -#define WS_TAB_IN_INDENT       040
 +#define WS_BLANK_AT_EOL         0100
 +#define WS_SPACE_BEFORE_TAB     0200
 +#define WS_INDENT_WITH_NON_TAB  0400
 +#define WS_CR_AT_EOL           01000
 +#define WS_BLANK_AT_EOF        02000
 +#define WS_TAB_IN_INDENT       04000
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 -#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 +#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
 +#define WS_TAB_WIDTH_MASK        077
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
@@@ -1128,7 -1091,6 +1129,7 @@@ extern void ws_check_emit(const char *l
  extern char *whitespace_error_string(unsigned ws);
  extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 +#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
@@@ -1136,15 -1098,6 +1137,15 @@@ void overlay_tree_on_cache(const char *
  
  char *alias_lookup(const char *alias);
  int split_cmdline(char *cmdline, const char ***argv);
 +/* Takes a negative value returned by split_cmdline */
 +const char *split_cmdline_strerror(int cmdline_errno);
 +
 +/* git.c */
 +struct startup_info {
 +      int have_repository;
 +      const char *prefix;
 +};
 +extern struct startup_info *startup_info;
  
  /* builtin/merge.c */
  int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
diff --combined config.c
index 304d71efc2df3cbec081677678db07dc63afc2b5,c06b7f6440defc7ebe6c79e0945ceaf4c9d48383..9b09375b65c3414544bfa02c1011d3a34eb568c7
+++ b/config.c
@@@ -8,7 -8,6 +8,7 @@@
  #include "cache.h"
  #include "exec_cmd.h"
  #include "strbuf.h"
 +#include "quote.h"
  
  #define MAXNAME (256)
  
@@@ -35,19 -34,6 +35,19 @@@ static void lowercase(char *p
                *p = tolower(*p);
  }
  
 +void git_config_push_parameter(const char *text)
 +{
 +      struct strbuf env = STRBUF_INIT;
 +      const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
 +      if (old) {
 +              strbuf_addstr(&env, old);
 +              strbuf_addch(&env, ' ');
 +      }
 +      sq_quote_buf(&env, text);
 +      setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1);
 +      strbuf_release(&env);
 +}
 +
  int git_config_parse_parameter(const char *text)
  {
        struct config_item *ct;
        return 0;
  }
  
 +int git_config_parse_environment(void) {
 +      const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
 +      char *envw;
 +      const char **argv = NULL;
 +      int nr = 0, alloc = 0;
 +      int i;
 +
 +      if (!env)
 +              return 0;
 +      /* sq_dequote will write over it */
 +      envw = xstrdup(env);
 +
 +      if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
 +              free(envw);
 +              return error("bogus format in " CONFIG_DATA_ENVIRONMENT);
 +      }
 +
 +      for (i = 0; i < nr; i++) {
 +              if (git_config_parse_parameter(argv[i]) < 0) {
 +                      error("bogus config parameter: %s", argv[i]);
 +                      free(argv);
 +                      free(envw);
 +                      return -1;
 +              }
 +      }
 +
 +      free(argv);
 +      free(envw);
 +      return 0;
 +}
 +
  static int get_next_char(void)
  {
        int c;
@@@ -410,7 -365,7 +410,7 @@@ unsigned long git_config_ulong(const ch
        return ret;
  }
  
 -int git_config_maybe_bool(const char *name, const char *value)
 +static int git_config_maybe_bool_text(const char *name, const char *value)
  {
        if (!value)
                return 1;
        return -1;
  }
  
 +int git_config_maybe_bool(const char *name, const char *value)
 +{
 +      long v = git_config_maybe_bool_text(name, value);
 +      if (0 <= v)
 +              return v;
 +      if (git_parse_long(value, &v))
 +              return !!v;
 +      return -1;
 +}
 +
  int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
  {
 -      int v = git_config_maybe_bool(name, value);
 +      int v = git_config_maybe_bool_text(name, value);
        if (0 <= v) {
                *is_bool = 1;
                return v;
@@@ -499,13 -444,6 +499,13 @@@ static int git_default_core_config(cons
                return 0;
        }
  
 +      if (!strcmp(var, "core.abbrevguard")) {
 +              unique_abbrev_extra_length = git_config_int(var, value);
 +              if (unique_abbrev_extra_length < 0)
 +                      unique_abbrev_extra_length = 0;
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
        if (!strcmp(var, "core.editor"))
                return git_config_string(&editor_program, var, value);
  
 +      if (!strcmp(var, "core.askpass"))
 +              return git_config_string(&askpass_program, var, value);
 +
        if (!strcmp(var, "core.excludesfile"))
                return git_config_pathname(&excludes_file, var, value);
  
+       if (!strcmp(var, "core.ignoredareprecious")) {
+               ignored_are_precious = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "core.whitespace")) {
                if (!value)
                        return config_error_nonbool(var);
                return 0;
        }
  
 +      if (!strcmp(var, "core.clockskew")) {
 +              if (!value || !strcmp(value, "none"))
 +                      core_clock_skew = -1;
 +              else
 +                      core_clock_skew = git_config_int(var, value);
 +              return 0;
 +      }
 +
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
@@@ -846,23 -778,17 +851,23 @@@ int git_config_global(void
  
  int git_config_from_parameters(config_fn_t fn, void *data)
  {
 +      static int loaded_environment;
        const struct config_item *ct;
 +
 +      if (!loaded_environment) {
 +              if (git_config_parse_environment() < 0)
 +                      return -1;
 +              loaded_environment = 1;
 +      }
        for (ct = config_parameters; ct; ct = ct->next)
                if (fn(ct->name, ct->value, data) < 0)
                        return -1;
        return 0;
  }
  
 -int git_config(config_fn_t fn, void *data)
 +int git_config_early(config_fn_t fn, void *data, const char *repo_config)
  {
        int ret = 0, found = 0;
 -      char *repo_config = NULL;
        const char *home = NULL;
  
        /* Setting $GIT_CONFIG makes git read _only_ the given config file. */
                free(user_config);
        }
  
 -      repo_config = git_pathdup("config");
 -      if (!access(repo_config, R_OK)) {
 +      if (repo_config && !access(repo_config, R_OK)) {
                ret += git_config_from_file(fn, repo_config, data);
                found += 1;
        }
 -      free(repo_config);
  
 -      if (config_parameters) {
 -              ret += git_config_from_parameters(fn, data);
 +      ret += git_config_from_parameters(fn, data);
 +      if (config_parameters)
                found += 1;
 -      }
  
 -      if (found == 0)
 -              return -1;
 +      return ret == 0 ? found : ret;
 +}
 +
 +int git_config(config_fn_t fn, void *data)
 +{
 +      char *repo_config = NULL;
 +      int ret;
 +
 +      repo_config = git_pathdup("config");
 +      ret = git_config_early(fn, data, repo_config);
 +      if (repo_config)
 +              free(repo_config);
        return ret;
  }
  
diff --combined environment.c
index 9564475f429312a467a020106f7443112367d5da,0667c1f170444dd03fdf11f24e5def481359d0cd..be744cc8a13401e75f10ecddc0b4f66c309d0f9c
@@@ -21,7 -21,6 +21,7 @@@ int prefer_symlink_refs
  int is_bare_repository_cfg = -1; /* unspecified */
  int log_all_ref_updates = -1; /* unspecified */
  int warn_ambiguous_refs = 1;
 +int unique_abbrev_extra_length;
  int repository_format_version;
  const char *git_commit_encoding;
  const char *git_log_output_encoding;
@@@ -31,6 -30,7 +31,7 @@@ const char *apply_default_ignorewhitesp
  int zlib_compression_level = Z_BEST_SPEED;
  int core_compression_level;
  int core_compression_seen;
+ int ignored_are_precious;
  int fsync_object_files;
  size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
  size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
@@@ -38,7 -38,6 +39,7 @@@ size_t delta_base_cache_limit = 16 * 10
  const char *pager_program;
  int pager_use_color = 1;
  const char *editor_program;
 +const char *askpass_program;
  const char *excludes_file;
  enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
  int read_replace_refs = 1;
@@@ -55,7 -54,6 +56,7 @@@ enum object_creation_mode object_creati
  char *notes_ref_name;
  int grafts_replace_parents = 1;
  int core_apply_sparse_checkout;
 +struct startup_info *startup_info;
  
  /* Parallel index stat data preload? */
  int core_preload_index = 0;
@@@ -65,7 -63,7 +66,7 @@@ char *git_work_tree_cfg
  static char *work_tree;
  
  static const char *git_dir;
 -static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 +static char *git_object_dir, *git_index_file, *git_graft_file;
  
  /*
   * Repository-local GIT_* environment variables
@@@ -75,7 -73,6 +76,7 @@@
  const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
        ALTERNATE_DB_ENVIRONMENT,
        CONFIG_ENVIRONMENT,
 +      CONFIG_DATA_ENVIRONMENT,
        DB_ENVIRONMENT,
        GIT_DIR_ENVIRONMENT,
        GIT_WORK_TREE_ENVIRONMENT,
  static void setup_git_env(void)
  {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
 -      if (!git_dir)
 +      git_dir = git_dir ? xstrdup(git_dir) : NULL;
 +      if (!git_dir) {
                git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
 +              git_dir = git_dir ? xstrdup(git_dir) : NULL;
 +      }
        if (!git_dir)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
        git_object_dir = getenv(DB_ENVIRONMENT);
                git_object_dir = xmalloc(strlen(git_dir) + 9);
                sprintf(git_object_dir, "%s/objects", git_dir);
        }
 -      git_refs_dir = xmalloc(strlen(git_dir) + 6);
 -      sprintf(git_refs_dir, "%s/refs", git_dir);
        git_index_file = getenv(INDEX_ENVIRONMENT);
        if (!git_index_file) {
                git_index_file = xmalloc(strlen(git_dir) + 7);
@@@ -139,20 -135,30 +140,20 @@@ static int git_work_tree_initialized
   */
  void set_git_work_tree(const char *new_work_tree)
  {
 -      if (is_bare_repository_cfg >= 0)
 -              die("cannot set work tree after initialization");
 +      if (git_work_tree_initialized) {
 +              new_work_tree = make_absolute_path(new_work_tree);
 +              if (strcmp(new_work_tree, work_tree))
 +                      die("internal error: work tree has already been set\n"
 +                          "Current worktree: %s\nNew worktree: %s",
 +                          work_tree, new_work_tree);
 +              return;
 +      }
        git_work_tree_initialized = 1;
 -      free(work_tree);
        work_tree = xstrdup(make_absolute_path(new_work_tree));
 -      is_bare_repository_cfg = 0;
  }
  
  const char *get_git_work_tree(void)
  {
 -      if (!git_work_tree_initialized) {
 -              work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
 -              /* core.bare = true overrides implicit and config work tree */
 -              if (!work_tree && is_bare_repository_cfg < 1) {
 -                      work_tree = git_work_tree_cfg;
 -                      /* make_absolute_path also normalizes the path */
 -                      if (work_tree && !is_absolute_path(work_tree))
 -                              work_tree = xstrdup(make_absolute_path(git_path("%s", work_tree)));
 -              } else if (work_tree)
 -                      work_tree = xstrdup(make_absolute_path(work_tree));
 -              git_work_tree_initialized = 1;
 -              if (work_tree)
 -                      is_bare_repository_cfg = 0;
 -      }
        return work_tree;
  }
  
@@@ -163,43 -169,6 +164,43 @@@ char *get_object_directory(void
        return git_object_dir;
  }
  
 +int odb_mkstemp(char *template, size_t limit, const char *pattern)
 +{
 +      int fd;
 +      /*
 +       * we let the umask do its job, don't try to be more
 +       * restrictive except to remove write permission.
 +       */
 +      int mode = 0444;
 +      snprintf(template, limit, "%s/%s",
 +               get_object_directory(), pattern);
 +      fd = git_mkstemp_mode(template, mode);
 +      if (0 <= fd)
 +              return fd;
 +
 +      /* slow path */
 +      /* some mkstemp implementations erase template on failure */
 +      snprintf(template, limit, "%s/%s",
 +               get_object_directory(), pattern);
 +      safe_create_leading_directories(template);
 +      return xmkstemp_mode(template, mode);
 +}
 +
 +int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
 +{
 +      int fd;
 +
 +      snprintf(name, namesz, "%s/pack/pack-%s.keep",
 +               get_object_directory(), sha1_to_hex(sha1));
 +      fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 +      if (0 <= fd)
 +              return fd;
 +
 +      /* slow path */
 +      safe_create_leading_directories(name);
 +      return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 +}
 +
  char *get_index_file(void)
  {
        if (!git_index_file)
@@@ -221,14 -190,3 +222,14 @@@ int set_git_dir(const char *path
        setup_git_env();
        return 0;
  }
 +
 +const char *get_log_output_encoding(void)
 +{
 +      return git_log_output_encoding ? git_log_output_encoding
 +              : get_commit_output_encoding();
 +}
 +
 +const char *get_commit_output_encoding(void)
 +{
 +      return git_commit_encoding ? git_commit_encoding : "UTF-8";
 +}