Merge branch 'ap/merge-backend-opts'
authorJunio C Hamano <gitster@pobox.com>
Thu, 21 Jan 2010 04:28:50 +0000 (20:28 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 21 Jan 2010 04:28:50 +0000 (20:28 -0800)
* ap/merge-backend-opts:
Document that merge strategies can now take their own options
Extend merge-subtree tests to test -Xsubtree=dir.
Make "subtree" part more orthogonal to the rest of merge-recursive.
pull: Fix parsing of -X<option>
Teach git-pull to pass -X<option> to git-merge
git merge -X<option>
git-merge-file --ours, --theirs

Conflicts:
git-compat-util.h

1  2 
Documentation/git-merge-file.txt
Documentation/merge-options.txt
builtin-merge.c
cache.h
git-compat-util.h
git-pull.sh
git.c
ll-merge.c
merge-recursive.c
merge-recursive.h
strbuf.c
index fa723d0513c5ca825a53508dc82990506f486638,b9d22764a6c7504905e637ccfbb781ad3769a5a0..234269ae59234de67ea2cce42edec83cebc400be
@@@ -10,20 -10,21 +10,21 @@@ SYNOPSI
  --------
  [verse]
  'git merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
-       [-p|--stdout] [-q|--quiet] <current-file> <base-file> <other-file>
+       [--ours|--theirs] [-p|--stdout] [-q|--quiet]
+       <current-file> <base-file> <other-file>
  
  
  DESCRIPTION
  -----------
 -'git-merge-file' incorporates all changes that lead from the `<base-file>`
 +'git merge-file' incorporates all changes that lead from the `<base-file>`
  to `<other-file>` into `<current-file>`. The result ordinarily goes into
 -`<current-file>`. 'git-merge-file' is useful for combining separate changes
 +`<current-file>`. 'git merge-file' is useful for combining separate changes
  to an original. Suppose `<base-file>` is the original, and both
  `<current-file>` and `<other-file>` are modifications of `<base-file>`,
 -then 'git-merge-file' combines both changes.
 +then 'git merge-file' combines both changes.
  
  A conflict occurs if both `<current-file>` and `<other-file>` have changes
 -in a common segment of lines. If a conflict is found, 'git-merge-file'
 +in a common segment of lines. If a conflict is found, 'git merge-file'
  normally outputs a warning and brackets the conflict with lines containing
  <<<<<<< and >>>>>>> markers. A typical conflict will look like this:
  
        >>>>>>> B
  
  If there are conflicts, the user should edit the result and delete one of
- the alternatives.
+ the alternatives.  When `--ours` or `--theirs` option is in effect, however,
+ these conflicts are resolved favouring lines from `<current-file>` or
+ lines from `<other-file>` respectively.
  
  The exit value of this program is negative on error, and the number of
  conflicts otherwise. If the merge was clean, the exit value is 0.
  
 -'git-merge-file' is designed to be a minimal clone of RCS 'merge'; that is, it
 +'git merge-file' is designed to be a minimal clone of RCS 'merge'; that is, it
  implements all of RCS 'merge''s functionality which is needed by
  linkgit:git[1].
  
@@@ -62,6 -65,11 +65,11 @@@ OPTION
  -q::
        Quiet; do not warn about conflicts.
  
+ --ours::
+ --theirs::
+       Instead of leaving conflicts in the file, resolve conflicts
+       favouring our (or their) side of the lines.
  
  EXAMPLES
  --------
index 5064bf83fa20c12cc699e5432e6dd6d29eda22de,22606f03f3e1233eff4ed2c88d45a6e07c026947..3b83dba1a0d8ad1436d15d164783f08593f54357
@@@ -59,8 -59,8 +59,8 @@@ option can be used to override --squash
        Use the given merge strategy; can be supplied more than
        once to specify them in the order they should be tried.
        If there is no `-s` option, a built-in list of strategies
 -      is used instead ('git-merge-recursive' when merging a single
 -      head, 'git-merge-octopus' otherwise).
 +      is used instead ('git merge-recursive' when merging a single
 +      head, 'git merge-octopus' otherwise).
  
  --summary::
  --no-summary::
@@@ -74,3 -74,8 +74,8 @@@
  -v::
  --verbose::
        Be verbose.
+ -X <option>::
+ --strategy-option=<option>::
+       Pass merge strategy specific option through to the merge
+       strategy.
diff --combined builtin-merge.c
index 9f60ffa2cd32fd78c04d81135cee42b584627e7c,3aa7ea4052c2eb3c41d955b24fdbd3cfd1ec1124..3aaec7bed76af9efdfe5647be0da64373db2011e
@@@ -24,7 -24,6 +24,7 @@@
  #include "rerere.h"
  #include "help.h"
  #include "merge-recursive.h"
 +#include "resolve-undo.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -51,9 -50,10 +51,11 @@@ static struct commit_list *remoteheads
  static unsigned char head[20], stash[20];
  static struct strategy **use_strategies;
  static size_t use_strategies_nr, use_strategies_alloc;
+ static const char **xopts;
+ static size_t xopts_nr, xopts_alloc;
  static const char *branch;
  static int verbosity;
 +static int allow_rerere_auto;
  
  static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@@ -73,7 -73,7 +75,7 @@@ static int option_parse_message(const s
        if (unset)
                strbuf_setlen(buf, 0);
        else if (arg) {
 -              strbuf_addf(buf, "%s\n\n", arg);
 +              strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg);
                have_message = 1;
        } else
                return error("switch `m' requires a value");
@@@ -148,6 -148,17 +150,17 @@@ static int option_parse_strategy(const 
        return 0;
  }
  
+ static int option_parse_x(const struct option *opt,
+                         const char *arg, int unset)
+ {
+       if (unset)
+               return 0;
+       ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
+       xopts[xopts_nr++] = xstrdup(arg);
+       return 0;
+ }
  static int option_parse_n(const struct option *opt,
                          const char *arg, int unset)
  {
@@@ -172,9 -183,10 +185,11 @@@ static struct option builtin_merge_opti
                "allow fast-forward (default)"),
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
                "abort if fast-forward is not possible"),
 +      OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
                "merge strategy to use", option_parse_strategy),
+       OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
+               "option for selected merge strategy", option_parse_x),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
                "message to be used for the merge commit (if any)",
                option_parse_message),
@@@ -537,7 -549,7 +552,7 @@@ static int try_merge_strategy(const cha
                              const char *head_arg)
  {
        const char **args;
-       int i = 0, ret;
+       int i = 0, x = 0, ret;
        struct commit_list *j;
        struct strbuf buf = STRBUF_INIT;
        int index_fd;
  
                init_merge_options(&o);
                if (!strcmp(strategy, "subtree"))
-                       o.subtree_merge = 1;
+                       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
+                               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 {
-               args = xmalloc((4 + commit_list_count(common) +
+               args = xmalloc((4 + xopts_nr + commit_list_count(common) +
                                        commit_list_count(remoteheads)) * sizeof(char *));
                strbuf_addf(&buf, "merge-%s", strategy);
                args[i++] = buf.buf;
+               for (x = 0; x < xopts_nr; x++) {
+                       char *s = xmalloc(strlen(xopts[x])+2+1);
+                       strcpy(s, "--");
+                       strcpy(s+2, xopts[x]);
+                       args[i++] = s;
+               }
                for (j = common; j; j = j->next)
                        args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
                args[i++] = "--";
                ret = run_command_v_opt(args, RUN_GIT_CMD);
                strbuf_release(&buf);
                i = 1;
+               for (x = 0; x < xopts_nr; x++)
+                       free((void *)args[i++]);
                for (j = common; j; j = j->next)
                        free((void *)args[i++]);
                i += 2;
                discard_cache();
                if (read_cache() < 0)
                        die("failed to read the cache");
 +              resolve_undo_clear();
                return ret;
        }
  }
@@@ -622,10 -654,11 +658,10 @@@ static void count_diff_files(struct dif
  
  static int count_unmerged_entries(void)
  {
 -      const struct index_state *state = &the_index;
        int i, ret = 0;
  
 -      for (i = 0; i < state->cache_nr; i++)
 -              if (ce_stage(state->cache[i]))
 +      for (i = 0; i < active_nr; i++)
 +              if (ce_stage(active_cache[i]))
                        ret++;
  
        return ret;
@@@ -659,7 -692,6 +695,7 @@@ static int checkout_fast_forward(unsign
        opts.verbose_update = 1;
        opts.merge = 1;
        opts.fn = twoway_merge;
 +      opts.msgs = get_porcelain_error_msgs();
  
        trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
@@@ -793,7 -825,7 +829,7 @@@ static int suggest_conflicts(void
                }
        }
        fclose(fp);
 -      rerere();
 +      rerere(allow_rerere_auto);
        printf("Automatic merge failed; "
                        "fix conflicts and then commit the result.\n");
        return 1;
  static struct commit *is_old_style_invocation(int argc, const char **argv)
  {
        struct commit *second_token = NULL;
 -      if (argc > 1) {
 +      if (argc > 2) {
                unsigned char second_sha1[20];
  
                if (get_sha1(argv[1], second_sha1))
@@@ -850,22 -882,12 +886,22 @@@ int cmd_merge(int argc, const char **ar
        const char *best_strategy = NULL, *wt_strategy = NULL;
        struct commit_list **remotes = &remoteheads;
  
 -      if (file_exists(git_path("MERGE_HEAD")))
 -              die("You have not concluded your merge. (MERGE_HEAD exists)");
 -      if (read_cache_unmerged())
 -              die("You are in the middle of a conflicted merge."
 -                              " (index unmerged)");
 +      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();
        /*
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
                 * codepath so we discard the error in this
                 * loop.
                 */
 -              for (i = 0; i < argc; i++)
 -                      merge_name(argv[i], &msg);
 -              fmt_merge_msg(option_log, &msg, &merge_msg);
 -              if (merge_msg.len)
 -                      strbuf_setlen(&merge_msg, merge_msg.len-1);
 +              if (!have_message) {
 +                      for (i = 0; i < argc; i++)
 +                              merge_name(argv[i], &msg);
 +                      fmt_merge_msg(option_log, &msg, &merge_msg);
 +                      if (merge_msg.len)
 +                              strbuf_setlen(&merge_msg, merge_msg.len-1);
 +              }
        }
  
        if (head_invalid || !argc)
diff --combined cache.h
index 5abcb09fcfe8d30f87e90f390b923fc07ec8e6e3,c6902d2c22985df7178c0cb48afe0b776b1dde77..4b15042c08afb2ba34ca6d08ebc5ca9b196ab7c8
+++ b/cache.h
@@@ -177,20 -177,15 +177,20 @@@ struct cache_entry 
  
  #define CE_HASHED    (0x100000)
  #define CE_UNHASHED  (0x200000)
 +#define CE_CONFLICTED (0x800000)
 +
 +/* Only remove in work directory, not index */
 +#define CE_WT_REMOVE (0x400000)
  
  /*
   * Extended on-disk flags
   */
  #define CE_INTENT_TO_ADD 0x20000000
 +#define CE_SKIP_WORKTREE 0x40000000
  /* CE_EXTENDED2 is for future extension */
  #define CE_EXTENDED2 0x80000000
  
 -#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
 +#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
  /*
   * Safeguard to avoid saving wrong flags:
@@@ -239,7 -234,6 +239,7 @@@ static inline size_t ce_namelen(const s
                            ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 +#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
@@@ -288,7 -282,6 +288,7 @@@ static inline int ce_to_dtype(const str
  struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
 +      struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
        struct cache_time timestamp;
        void *alloc;
@@@ -343,9 -336,6 +343,9 @@@ static inline void remove_name_hash(str
  #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))
 +#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
 +#define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
 +#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
  #endif
  
  enum object_type {
@@@ -455,6 -445,7 +455,6 @@@ extern int index_name_pos(const struct 
  #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);
  extern int remove_index_entry_at(struct index_state *, int pos);
  extern void remove_marked_cache_entries(struct index_state *istate);
@@@ -473,9 -464,7 +473,9 @@@ extern int index_name_is_other(const st
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
  /* do not check the contents but report dirty on racily-clean entries */
 -#define CE_MATCH_RACY_IS_DIRTY        02
 +#define CE_MATCH_RACY_IS_DIRTY                02
 +/* do stat comparison even if CE_SKIP_WORKTREE is true */
 +#define CE_MATCH_IGNORE_SKIP_WORKTREE 04
  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);
  
@@@ -484,6 -473,9 +484,6 @@@ extern int index_fd(unsigned char *sha1
  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);
  
 -/* "careful lstat()" */
 -extern int check_path(const char *path, int len, struct stat *st, int skiplen);
 -
  #define REFRESH_REALLY                0x0001  /* ignore_valid */
  #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
  #define REFRESH_QUIET         0x0004  /* be quiet about it */
@@@ -537,7 -529,6 +537,7 @@@ extern int auto_crlf
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
 +extern int core_apply_sparse_checkout;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -627,6 -618,7 +627,6 @@@ static inline void hashclr(unsigned cha
  {
        memset(hash, 0, 20);
  }
 -extern int is_empty_blob_sha1(const unsigned char *sha1);
  
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
@@@ -696,6 -688,7 +696,6 @@@ extern int has_sha1_pack(const unsigne
  extern int has_sha1_file(const unsigned char *sha1);
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
 -extern int has_pack_file(const unsigned char *sha1);
  extern int has_pack_index(const unsigned char *sha1);
  
  extern const signed char hexval_table[256];
@@@ -709,11 -702,7 +709,11 @@@ static inline unsigned int hexval(unsig
  #define DEFAULT_ABBREV 7
  
  extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
 +extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
 +static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
 +{
 +      return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
 +}
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
@@@ -721,7 -710,6 +721,7 @@@ extern const char *resolve_ref(const ch
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  extern int interpret_branch_name(const char *str, struct strbuf *);
 +extern int get_sha1_mb(const char *str, unsigned char *sha1);
  
  extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
  extern const char *ref_rev_parse_rules[];
@@@ -796,6 -784,8 +796,6 @@@ extern int has_symlink_leading_path(con
  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 has_dirs_only_path(const char *name, int len, int prefix_len);
 -extern void invalidate_lstat_cache(const char *name, int len);
 -extern void clear_lstat_cache(void);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
@@@ -935,11 -925,7 +935,11 @@@ extern const char *config_exclusive_fil
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
  extern char git_default_name[MAX_GITNAME];
 +#define IDENT_NAME_GIVEN 01
 +#define IDENT_MAIL_GIVEN 02
 +#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
  extern int user_ident_explicitly_given;
 +extern int user_ident_sufficiently_given(void);
  
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
@@@ -1007,6 -993,7 +1007,7 @@@ extern int diff_auto_refresh_index
  
  /* match-trees.c */
  void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
+ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
  
  /*
   * whitespace rules.
diff --combined git-compat-util.h
index 60c8432f854f345b77b469d64dd1429cee74d30d,f64cc454e7b63b7922bd425218dfdb52ba5c6038..aff627a85a812bb782e68cd27d06eb42d7dac537
@@@ -96,7 -96,6 +96,7 @@@
  #include <sys/poll.h>
  #include <sys/socket.h>
  #include <sys/ioctl.h>
 +#include <termios.h>
  #ifndef NO_SYS_SELECT_H
  #include <sys/select.h>
  #endif
@@@ -199,6 -198,8 +199,7 @@@ extern void warning(const char *err, ..
  extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
  
  extern int prefixcmp(const char *str, const char *prefix);
 -extern time_t tm_to_time_t(const struct tm *tm);
+ extern int suffixcmp(const char *str, const char *suffix);
  
  static inline const char *skip_prefix(const char *str, const char *prefix)
  {
diff --combined git-pull.sh
index 54ce0af2d4c718b2a5d7367219e6572917ce567f,fc3536bdf1dc3b29b8d5c705891be8d1ae3db068..2de4c3aa70c10203aebe4e117b30422e39f41e02
@@@ -13,32 -13,12 +13,33 @@@ set_reflog_action "pull $*
  require_work_tree
  cd_to_toplevel
  
 -test -z "$(git ls-files -u)" ||
 -      die "You are in the middle of a conflicted merge."
 +
 +die_conflict () {
 +    git diff-index --cached --name-status -r --ignore-submodules HEAD --
 +    if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
 +      die "Pull is not possible because you have unmerged files.
 +Please, fix them up in the work tree, and then use 'git add/rm <file>'
 +as appropriate to mark resolution, or use 'git commit -a'."
 +    else
 +      die "Pull is not possible because you have unmerged files."
 +    fi
 +}
 +
 +die_merge () {
 +    if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
 +      die "You have not concluded your merge (MERGE_HEAD exists).
 +Please, commit your changes before you can merge."
 +    else
 +      die "You have not concluded your merge (MERGE_HEAD exists)."
 +    fi
 +}
 +
 +test -z "$(git ls-files -u)" || die_conflict
 +test -f "$GIT_DIR/MERGE_HEAD" && die_merge
  
  strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
  log_arg= verbosity=
+ merge_args=
  curr_branch=$(git symbolic-ref -q HEAD)
  curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
  rebase=$(git config --bool branch.$curr_branch_short.rebase)
@@@ -83,6 -63,18 +84,18 @@@ d
                esac
                strategy_args="${strategy_args}-s $strategy "
                ;;
+       -X*)
+               case "$#,$1" in
+               1,-X)
+                       usage ;;
+               *,-X)
+                       xx="-X $(git rev-parse --sq-quote "$2")"
+                       shift ;;
+               *,*)
+                       xx=$(git rev-parse --sq-quote "$1") ;;
+               esac
+               merge_args="$merge_args$xx "
+               ;;
        -r|--r|--re|--reb|--reba|--rebas|--rebase)
                rebase=true
                ;;
@@@ -112,63 -104,45 +125,63 @@@ error_on_no_merge_candidates () 
                esac
        done
  
 +      if test true = "$rebase"
 +      then
 +              op_type=rebase
 +              op_prep=against
 +      else
 +              op_type=merge
 +              op_prep=with
 +      fi
 +
        curr_branch=${curr_branch#refs/heads/}
        upstream=$(git config "branch.$curr_branch.merge")
        remote=$(git config "branch.$curr_branch.remote")
  
        if [ $# -gt 1 ]; then
 -              echo "There are no candidates for merging in the refs that you just fetched."
 +              if [ "$rebase" = true ]; then
 +                      printf "There is no candidate for rebasing against "
 +              else
 +                      printf "There are no candidates for merging "
 +              fi
 +              echo "among the refs that you just fetched."
                echo "Generally this means that you provided a wildcard refspec which had no"
                echo "matches on the remote end."
        elif [ $# -gt 0 ] && [ "$1" != "$remote" ]; then
                echo "You asked to pull from the remote '$1', but did not specify"
 -              echo "a branch to merge. Because this is not the default configured remote"
 +              echo "a branch. Because this is not the default configured remote"
                echo "for your current branch, you must specify a branch on the command line."
        elif [ -z "$curr_branch" ]; then
                echo "You are not currently on a branch, so I cannot use any"
                echo "'branch.<branchname>.merge' in your configuration file."
 -              echo "Please specify which branch you want to merge on the command"
 +              echo "Please specify which remote branch you want to use on the command"
                echo "line and try again (e.g. 'git pull <repository> <refspec>')."
                echo "See git-pull(1) for details."
        elif [ -z "$upstream" ]; then
                echo "You asked me to pull without telling me which branch you"
 -              echo "want to merge with, and 'branch.${curr_branch}.merge' in"
 -              echo "your configuration file does not tell me either.  Please"
 -              echo "specify which branch you want to merge on the command line and"
 +              echo "want to $op_type $op_prep, and 'branch.${curr_branch}.merge' in"
 +              echo "your configuration file does not tell me, either. Please"
 +              echo "specify which branch you want to use on the command line and"
                echo "try again (e.g. 'git pull <repository> <refspec>')."
                echo "See git-pull(1) for details."
                echo
 -              echo "If you often merge with the same branch, you may want to"
 -              echo "configure the following variables in your configuration"
 -              echo "file:"
 +              echo "If you often $op_type $op_prep the same branch, you may want to"
 +              echo "use something like the following in your configuration file:"
 +              echo
 +              echo "    [branch \"${curr_branch}\"]"
 +              echo "    remote = <nickname>"
 +              echo "    merge = <remote-ref>"
 +              test rebase = "$op_type" &&
 +                      echo "    rebase = true"
                echo
 -              echo "    branch.${curr_branch}.remote = <nickname>"
 -              echo "    branch.${curr_branch}.merge = <remote-ref>"
 -              echo "    remote.<nickname>.url = <url>"
 -              echo "    remote.<nickname>.fetch = <refspec>"
 +              echo "    [remote \"<nickname>\"]"
 +              echo "    url = <url>"
 +              echo "    fetch = <refspec>"
                echo
                echo "See git-config(1) for details."
        else
 -              echo "Your configuration specifies to merge the ref '${upstream#refs/heads/}' from the"
 -              echo "remote, but no such ref was fetched."
 +              echo "Your configuration specifies to $op_type $op_prep the ref '${upstream#refs/heads/}'"
 +              echo "from the remote, but no such ref was fetched."
        fi
        exit 1
  }
@@@ -254,8 -228,15 +267,15 @@@ the
  fi
  
  merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
- test true = "$rebase" &&
-       exec git-rebase $diffstat $strategy_args --onto $merge_head \
-       ${oldremoteref:-$merge_head}
- exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \
-       "$merge_name" HEAD $merge_head $verbosity
+ case "$rebase" in
+ true)
+       eval="git-rebase $diffstat $strategy_args $merge_args"
+       eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
+       ;;
+ *)
+       eval="git-merge $diffstat $no_commit $squash $no_ff $ff_only"
+       eval="$eval  $log_arg $strategy_args $merge_args"
+       eval="$eval \"$merge_name\" HEAD $merge_head $verbosity"
+       ;;
+ esac
+ eval "exec $eval"
diff --combined git.c
index ad074735fcf4d37041da65653967a482b6070bfe,4735f11083ea753af319f1d334bcd89084d475db..194471f5b1d96658a9745538e533018f00a4f989
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -317,7 -317,7 +317,7 @@@ static void handle_internal_command(in
                { "fsck-objects", cmd_fsck, RUN_SETUP },
                { "gc", cmd_gc, RUN_SETUP },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
 -              { "grep", cmd_grep, RUN_SETUP | USE_PAGER },
 +              { "grep", cmd_grep, USE_PAGER },
                { "help", cmd_help },
                { "init", cmd_init_db },
                { "init-db", cmd_init_db },
                { "merge-file", cmd_merge_file },
                { "merge-ours", cmd_merge_ours, RUN_SETUP },
                { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+               { "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
+               { "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
                { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
                { "mktree", cmd_mktree, RUN_SETUP },
                { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
diff --combined ll-merge.c
index 18511e281fe7435c2ae93ba3faf502cc606e62b9,45f7e0190a13871e70d784923c709447e127fc1c..070d66dd402bcb7ba51d69974c21a255aa93558b
@@@ -18,7 -18,7 +18,7 @@@ typedef int (*ll_merge_fn)(const struc
                           mmfile_t *orig,
                           mmfile_t *src1, const char *name1,
                           mmfile_t *src2, const char *name2,
-                          int virtual_ancestor);
+                          int flag);
  
  struct ll_merge_driver {
        const char *name;
@@@ -38,14 -38,14 +38,14 @@@ static int ll_binary_merge(const struc
                           mmfile_t *orig,
                           mmfile_t *src1, const char *name1,
                           mmfile_t *src2, const char *name2,
-                          int virtual_ancestor)
+                          int flag)
  {
        /*
         * The tentative merge result is "ours" for the final round,
         * or common ancestor for an internal merge.  Still return
         * "conflicted merge" status.
         */
-       mmfile_t *stolen = virtual_ancestor ? orig : src1;
+       mmfile_t *stolen = (flag & 01) ? orig : src1;
  
        result->ptr = stolen->ptr;
        result->size = stolen->size;
@@@ -59,10 -59,11 +59,11 @@@ static int ll_xdl_merge(const struct ll
                        mmfile_t *orig,
                        mmfile_t *src1, const char *name1,
                        mmfile_t *src2, const char *name2,
-                       int virtual_ancestor)
+                       int flag)
  {
        xpparam_t xpp;
        int style = 0;
+       int favor = (flag >> 1) & 03;
  
        if (buffer_is_binary(orig->ptr, orig->size) ||
            buffer_is_binary(src1->ptr, src1->size) ||
@@@ -72,8 -73,7 +73,7 @@@
                return ll_binary_merge(drv_unused, result,
                                       path,
                                       orig, src1, name1,
-                                      src2, name2,
-                                      virtual_ancestor);
+                                      src2, name2, flag);
        }
  
        memset(&xpp, 0, sizeof(xpp));
@@@ -82,7 -82,7 +82,7 @@@
        return xdl_merge(orig,
                         src1, name1,
                         src2, name2,
-                        &xpp, XDL_MERGE_ZEALOUS | style,
+                        &xpp, XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS, style, favor),
                         result);
  }
  
@@@ -92,7 -92,7 +92,7 @@@ static int ll_union_merge(const struct 
                          mmfile_t *orig,
                          mmfile_t *src1, const char *name1,
                          mmfile_t *src2, const char *name2,
-                         int virtual_ancestor)
+                         int flag)
  {
        char *src, *dst;
        long size;
        git_xmerge_style = 0;
        status = ll_xdl_merge(drv_unused, result, path_unused,
                              orig, src1, NULL, src2, NULL,
-                             virtual_ancestor);
+                             flag);
        git_xmerge_style = saved_style;
        if (status <= 0)
                return status;
@@@ -165,7 -165,7 +165,7 @@@ static int ll_ext_merge(const struct ll
                        mmfile_t *orig,
                        mmfile_t *src1, const char *name1,
                        mmfile_t *src2, const char *name2,
-                       int virtual_ancestor)
+                       int flag)
  {
        char temp[3][50];
        struct strbuf cmd = STRBUF_INIT;
                { "B", temp[2] },
                { NULL }
        };
 -      const char *args[] = { "sh", "-c", NULL, NULL };
 +      const char *args[] = { NULL, NULL };
        int status, fd, i;
        struct stat st;
  
  
        strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
  
 -      args[2] = cmd.buf;
 -      status = run_command_v_opt(args, 0);
 +      args[0] = cmd.buf;
 +      status = run_command_v_opt(args, RUN_USING_SHELL);
        fd = open(temp[1], O_RDONLY);
        if (fd < 0)
                goto bad;
@@@ -356,10 -356,11 +356,11 @@@ int ll_merge(mmbuffer_t *result_buf
             mmfile_t *ancestor,
             mmfile_t *ours, const char *our_label,
             mmfile_t *theirs, const char *their_label,
-            int virtual_ancestor)
+            int flag)
  {
        const char *ll_driver_name;
        const struct ll_merge_driver *driver;
+       int virtual_ancestor = flag & 01;
  
        ll_driver_name = git_path_check_merge(path);
        driver = find_ll_merge_driver(ll_driver_name);
        return driver->fn(driver, result_buf, path,
                          ancestor,
                          ours, our_label,
-                         theirs, their_label, virtual_ancestor);
+                         theirs, their_label, flag);
  }
diff --combined merge-recursive.c
index dd4fbd0e6bc22f2e5f5667205b47165f8aebbbd0,0af77cfa56b266d50b650c173f27436271240033..1239647fc0bcda68e744f5f3040c99314c8b8714
  #include "merge-recursive.h"
  #include "dir.h"
  
- static struct tree *shift_tree_object(struct tree *one, struct tree *two)
+ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
+                                     const char *subtree_shift)
  {
        unsigned char shifted[20];
  
-       /*
-        * NEEDSWORK: this limits the recursion depth to hardcoded
-        * value '2' to avoid excessive overhead.
-        */
-       shift_tree(one->object.sha1, two->object.sha1, shifted, 2);
+       if (!*subtree_shift) {
+               shift_tree(one->object.sha1, two->object.sha1, shifted, 0);
+       } else {
+               shift_tree_by(one->object.sha1, two->object.sha1, shifted,
+                             subtree_shift);
+       }
        if (!hashcmp(two->object.sha1, shifted))
                return two;
        return lookup_tree(shifted);
@@@ -172,6 -174,23 +174,6 @@@ static int git_merge_trees(int index_on
        int rc;
        struct tree_desc t[3];
        struct unpack_trees_options opts;
 -      struct unpack_trees_error_msgs msgs = {
 -              /* would_overwrite */
 -              "Your local changes to '%s' would be overwritten by merge.  Aborting.",
 -              /* not_uptodate_file */
 -              "Your local changes to '%s' would be overwritten by merge.  Aborting.",
 -              /* not_uptodate_dir */
 -              "Updating '%s' would lose untracked files in it.  Aborting.",
 -              /* would_lose_untracked */
 -              "Untracked working tree file '%s' would be %s by merge.  Aborting",
 -              /* bind_overlap -- will not happen here */
 -              NULL,
 -      };
 -      if (advice_commit_before_merge) {
 -              msgs.would_overwrite = msgs.not_uptodate_file =
 -                      "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
 -                      "Please, commit your changes or stash them before you can merge.";
 -      }
  
        memset(&opts, 0, sizeof(opts));
        if (index_only)
        opts.fn = threeway_merge;
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
 -      opts.msgs = msgs;
 +      opts.msgs = get_porcelain_error_msgs();
  
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@@ -625,6 -644,23 +627,23 @@@ static int merge_3way(struct merge_opti
        mmfile_t orig, src1, src2;
        char *name1, *name2;
        int merge_status;
+       int favor;
+       if (o->call_depth)
+               favor = 0;
+       else {
+               switch (o->recursive_variant) {
+               case MERGE_RECURSIVE_OURS:
+                       favor = XDL_MERGE_FAVOR_OURS;
+                       break;
+               case MERGE_RECURSIVE_THEIRS:
+                       favor = XDL_MERGE_FAVOR_THEIRS;
+                       break;
+               default:
+                       favor = 0;
+                       break;
+               }
+       }
  
        if (strcmp(a->path, b->path)) {
                name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
  
        merge_status = ll_merge(result_buf, a->path, &orig,
                                &src1, name1, &src2, name2,
-                               o->call_depth);
+                               (!!o->call_depth) | (favor << 1));
  
        free(name1);
        free(name2);
@@@ -1171,28 -1207,6 +1190,28 @@@ static int process_entry(struct merge_o
        return clean_merge;
  }
  
 +struct unpack_trees_error_msgs get_porcelain_error_msgs(void)
 +{
 +      struct unpack_trees_error_msgs msgs = {
 +              /* would_overwrite */
 +              "Your local changes to '%s' would be overwritten by merge.  Aborting.",
 +              /* not_uptodate_file */
 +              "Your local changes to '%s' would be overwritten by merge.  Aborting.",
 +              /* not_uptodate_dir */
 +              "Updating '%s' would lose untracked files in it.  Aborting.",
 +              /* would_lose_untracked */
 +              "Untracked working tree file '%s' would be %s by merge.  Aborting",
 +              /* bind_overlap -- will not happen here */
 +              NULL,
 +      };
 +      if (advice_commit_before_merge) {
 +              msgs.would_overwrite = msgs.not_uptodate_file =
 +                      "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
 +                      "Please, commit your changes or stash them before you can merge.";
 +      }
 +      return msgs;
 +}
 +
  int merge_trees(struct merge_options *o,
                struct tree *head,
                struct tree *merge,
  {
        int code, clean;
  
-       if (o->subtree_merge) {
-               merge = shift_tree_object(head, merge);
-               common = shift_tree_object(head, common);
+       if (o->subtree_shift) {
+               merge = shift_tree_object(head, merge, o->subtree_shift);
+               common = shift_tree_object(head, common, o->subtree_shift);
        }
  
        if (sha_eq(common->object.sha1, merge->object.sha1)) {
diff --combined merge-recursive.h
index d8bc7299ee3a70484b2436afd3799fe6f7608422,e63df9d63b23f2c5458620007ed6e770aa2b1047..be8410ad1803bc10e5dbf74f39eecdfed53469b1
@@@ -6,7 -6,12 +6,12 @@@
  struct merge_options {
        const char *branch1;
        const char *branch2;
-       unsigned subtree_merge : 1;
+       enum {
+               MERGE_RECURSIVE_NORMAL = 0,
+               MERGE_RECURSIVE_OURS,
+               MERGE_RECURSIVE_THEIRS,
+       } recursive_variant;
+       const char *subtree_shift;
        unsigned buffer_output : 1;
        int verbosity;
        int diff_rename_limit;
@@@ -17,9 -22,6 +22,9 @@@
        struct string_list current_directory_set;
  };
  
 +/* Return a list of user-friendly error messages to be used by merge */
 +struct unpack_trees_error_msgs get_porcelain_error_msgs(void);
 +
  /* merge_trees() but with recursive ancestor consolidation */
  int merge_recursive(struct merge_options *o,
                    struct commit *h1,
diff --combined strbuf.c
index 67448b7b07aa9d07b0c8cc250e3e0fad64c8fb5e,d71a623b9c7a6f5c5d786a692eec852f54a1a317..bc3a0802ea7e7b1743602972de182391b4bf0b3f
+++ b/strbuf.c
@@@ -10,6 -10,15 +10,15 @@@ int prefixcmp(const char *str, const ch
                        return (unsigned char)*prefix - (unsigned char)*str;
  }
  
+ int suffixcmp(const char *str, const char *suffix)
+ {
+       int len = strlen(str), suflen = strlen(suffix);
+       if (len < suflen)
+               return -1;
+       else
+               return strcmp(str + len - suflen, suffix);
+ }
  /*
   * Used as the default ->buf value, so that people can always assume
   * buf is non NULL and ->buf is NUL terminated even for a freshly
@@@ -91,6 -100,13 +100,6 @@@ void strbuf_ltrim(struct strbuf *sb
        sb->buf[sb->len] = '\0';
  }
  
 -void strbuf_tolower(struct strbuf *sb)
 -{
 -      int i;
 -      for (i = 0; i < sb->len; i++)
 -              sb->buf[i] = tolower(sb->buf[i]);
 -}
 -
  struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
  {
        int alloc = 2, pos = 0;
@@@ -220,12 -236,6 +229,12 @@@ void strbuf_expand(struct strbuf *sb, c
                        break;
                format = percent + 1;
  
 +              if (*format == '%') {
 +                      strbuf_addch(sb, '%');
 +                      format++;
 +                      continue;
 +              }
 +
                consumed = fn(sb, format, context);
                if (consumed)
                        format += consumed;
@@@ -250,17 -260,6 +259,17 @@@ size_t strbuf_expand_dict_cb(struct str
        return 0;
  }
  
 +void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
 +{
 +      int i, len = src->len;
 +
 +      for (i = 0; i < len; i++) {
 +              if (src->buf[i] == '%')
 +                      strbuf_addch(dst, '%');
 +              strbuf_addch(dst, src->buf[i]);
 +      }
 +}
 +
  size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
  {
        size_t res;