]> rtime.felk.cvut.cz Git - git.git/commitdiff
Merge branch 'lt/unpack-trees'
authorJunio C Hamano <gitster@pobox.com>
Wed, 12 Mar 2008 05:13:44 +0000 (22:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 12 Mar 2008 05:13:44 +0000 (22:13 -0700)
* lt/unpack-trees:
  unpack_trees(): fix diff-index regression.
  traverse_trees_recursive(): propagate merge errors up
  unpack_trees(): minor memory leak fix in unused destination index
  Make 'unpack_trees()' have a separate source and destination index
  Make 'unpack_trees()' take the index to work on as an argument
  Add 'const' where appropriate to index handling functions
  Fix tree-walking compare_entry() in the presense of --prefix
  Move 'unpack_trees()' over to 'traverse_trees()' interface
  Make 'traverse_trees()' traverse conflicting DF entries in parallel
  Add return value to 'traverse_tree()' callback
  Make 'traverse_tree()' use linked structure rather than 'const char *base'
  Add 'df_name_compare()' helper function

14 files changed:
Documentation/git-pull.txt
Documentation/git-rebase.txt
Makefile
builtin-merge-recursive.c
builtin-tag.c
cache.h
contrib/completion/git-completion.bash
git-rebase.sh
git-svn.perl
ll-merge.c [new file with mode: 0644]
ll-merge.h [new file with mode: 0644]
sha1_name.c
t/t0021-conversion.sh
t/t7005-editor.sh

index 737894390d8033bf470b3bfe3aeade858f38717e..3405ca09e85e9c81b9d48c753a2f117e7edca4eb 100644 (file)
@@ -21,6 +21,8 @@ Note that you can use `.` (current directory) as the
 <repository> to pull from the local repository -- this is useful
 when merging local branches into the current branch.
 
+Also note that options meant for `git-pull` itself and underlying
+`git-merge` must be given before the options meant for `git-fetch`.
 
 OPTIONS
 -------
index 4b10304740f5035095c19be05859ae0b9205e9a0..e0412e0866db8f80e8bbc8f33caaa659461d90aa 100644 (file)
@@ -262,8 +262,7 @@ hook if one exists.  You can use this hook to do sanity checks and
 reject the rebase if it isn't appropriate.  Please see the template
 pre-rebase hook script for an example.
 
-You must be in the top directory of your project to start (or continue)
-a rebase.  Upon completion, <branch> will be the current branch.
+Upon completion, <branch> will be the current branch.
 
 INTERACTIVE MODE
 ----------------
index 7fbb81578264e47c83857e864e58b32c4f81a5fa..514738046f94abc9ab16bfebc3b0d3e0118b5a5f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -308,7 +308,7 @@ LIB_H = \
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
        utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
-       mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h fsck.h \
+       mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h ll-merge.h fsck.h \
        pack-revindex.h
 
 DIFF_OBJS = \
@@ -333,7 +333,7 @@ LIB_OBJS = \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
        convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
        transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
-       alias.o fsck.o pack-revindex.o
+       ll-merge.o alias.o fsck.o pack-revindex.o
 
 BUILTIN_OBJS = \
        builtin-add.o \
index fa02bb5a804a58ea0831e8476aee9a80914667f4..5c7fbb2599d07ad7415ae8dfef205356ac3b0bbb 100644 (file)
 #include "tree-walk.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "run-command.h"
 #include "tag.h"
 #include "unpack-trees.h"
 #include "path-list.h"
 #include "xdiff-interface.h"
+#include "ll-merge.h"
 #include "interpolate.h"
 #include "attr.h"
 #include "merge-recursive.h"
@@ -617,364 +617,16 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
        mm->size = size;
 }
 
-/*
- * Customizable low-level merge drivers support.
- */
-
-struct ll_merge_driver;
-typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
-                          const char *path,
-                          mmfile_t *orig,
-                          mmfile_t *src1, const char *name1,
-                          mmfile_t *src2, const char *name2,
-                          mmbuffer_t *result);
-
-struct ll_merge_driver {
-       const char *name;
-       const char *description;
-       ll_merge_fn fn;
-       const char *recursive;
-       struct ll_merge_driver *next;
-       char *cmdline;
-};
-
-/*
- * Built-in low-levels
- */
-static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
-                          const char *path_unused,
-                          mmfile_t *orig,
-                          mmfile_t *src1, const char *name1,
-                          mmfile_t *src2, const char *name2,
-                          mmbuffer_t *result)
-{
-       /*
-        * 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 = index_only ? orig : src1;
-
-       result->ptr = stolen->ptr;
-       result->size = stolen->size;
-       stolen->ptr = NULL;
-       return 1;
-}
-
-static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
-                       const char *path_unused,
-                       mmfile_t *orig,
-                       mmfile_t *src1, const char *name1,
-                       mmfile_t *src2, const char *name2,
-                       mmbuffer_t *result)
-{
-       xpparam_t xpp;
-
-       if (buffer_is_binary(orig->ptr, orig->size) ||
-           buffer_is_binary(src1->ptr, src1->size) ||
-           buffer_is_binary(src2->ptr, src2->size)) {
-               warning("Cannot merge binary files: %s vs. %s\n",
-                       name1, name2);
-               return ll_binary_merge(drv_unused, path_unused,
-                                      orig, src1, name1,
-                                      src2, name2,
-                                      result);
-       }
-
-       memset(&xpp, 0, sizeof(xpp));
-       return xdl_merge(orig,
-                        src1, name1,
-                        src2, name2,
-                        &xpp, XDL_MERGE_ZEALOUS,
-                        result);
-}
-
-static int ll_union_merge(const struct ll_merge_driver *drv_unused,
-                         const char *path_unused,
-                         mmfile_t *orig,
-                         mmfile_t *src1, const char *name1,
-                         mmfile_t *src2, const char *name2,
-                         mmbuffer_t *result)
-{
-       char *src, *dst;
-       long size;
-       const int marker_size = 7;
-
-       int status = ll_xdl_merge(drv_unused, path_unused,
-                                 orig, src1, NULL, src2, NULL, result);
-       if (status <= 0)
-               return status;
-       size = result->size;
-       src = dst = result->ptr;
-       while (size) {
-               char ch;
-               if ((marker_size < size) &&
-                   (*src == '<' || *src == '=' || *src == '>')) {
-                       int i;
-                       ch = *src;
-                       for (i = 0; i < marker_size; i++)
-                               if (src[i] != ch)
-                                       goto not_a_marker;
-                       if (src[marker_size] != '\n')
-                               goto not_a_marker;
-                       src += marker_size + 1;
-                       size -= marker_size + 1;
-                       continue;
-               }
-       not_a_marker:
-               do {
-                       ch = *src++;
-                       *dst++ = ch;
-                       size--;
-               } while (ch != '\n' && size);
-       }
-       result->size = dst - result->ptr;
-       return 0;
-}
-
-#define LL_BINARY_MERGE 0
-#define LL_TEXT_MERGE 1
-#define LL_UNION_MERGE 2
-static struct ll_merge_driver ll_merge_drv[] = {
-       { "binary", "built-in binary merge", ll_binary_merge },
-       { "text", "built-in 3-way text merge", ll_xdl_merge },
-       { "union", "built-in union merge", ll_union_merge },
-};
-
-static void create_temp(mmfile_t *src, char *path)
-{
-       int fd;
-
-       strcpy(path, ".merge_file_XXXXXX");
-       fd = xmkstemp(path);
-       if (write_in_full(fd, src->ptr, src->size) != src->size)
-               die("unable to write temp-file");
-       close(fd);
-}
-
-/*
- * User defined low-level merge driver support.
- */
-static int ll_ext_merge(const struct ll_merge_driver *fn,
-                       const char *path,
-                       mmfile_t *orig,
-                       mmfile_t *src1, const char *name1,
-                       mmfile_t *src2, const char *name2,
-                       mmbuffer_t *result)
-{
-       char temp[3][50];
-       char cmdbuf[2048];
-       struct interp table[] = {
-               { "%O" },
-               { "%A" },
-               { "%B" },
-       };
-       struct child_process child;
-       const char *args[20];
-       int status, fd, i;
-       struct stat st;
-
-       if (fn->cmdline == NULL)
-               die("custom merge driver %s lacks command line.", fn->name);
-
-       result->ptr = NULL;
-       result->size = 0;
-       create_temp(orig, temp[0]);
-       create_temp(src1, temp[1]);
-       create_temp(src2, temp[2]);
-
-       interp_set_entry(table, 0, temp[0]);
-       interp_set_entry(table, 1, temp[1]);
-       interp_set_entry(table, 2, temp[2]);
-
-       output(1, "merging %s using %s", path,
-              fn->description ? fn->description : fn->name);
-
-       interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
-
-       memset(&child, 0, sizeof(child));
-       child.argv = args;
-       args[0] = "sh";
-       args[1] = "-c";
-       args[2] = cmdbuf;
-       args[3] = NULL;
-
-       status = run_command(&child);
-       if (status < -ERR_RUN_COMMAND_FORK)
-               ; /* failure in run-command */
-       else
-               status = -status;
-       fd = open(temp[1], O_RDONLY);
-       if (fd < 0)
-               goto bad;
-       if (fstat(fd, &st))
-               goto close_bad;
-       result->size = st.st_size;
-       result->ptr = xmalloc(result->size + 1);
-       if (read_in_full(fd, result->ptr, result->size) != result->size) {
-               free(result->ptr);
-               result->ptr = NULL;
-               result->size = 0;
-       }
- close_bad:
-       close(fd);
- bad:
-       for (i = 0; i < 3; i++)
-               unlink(temp[i]);
-       return status;
-}
-
-/*
- * merge.default and merge.driver configuration items
- */
-static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
-static const char *default_ll_merge;
-
-static int read_merge_config(const char *var, const char *value)
-{
-       struct ll_merge_driver *fn;
-       const char *ep, *name;
-       int namelen;
-
-       if (!strcmp(var, "merge.default")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               default_ll_merge = strdup(value);
-               return 0;
-       }
-
-       /*
-        * We are not interested in anything but "merge.<name>.variable";
-        * especially, we do not want to look at variables such as
-        * "merge.summary", "merge.tool", and "merge.verbosity".
-        */
-       if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
-               return 0;
-
-       /*
-        * Find existing one as we might be processing merge.<name>.var2
-        * after seeing merge.<name>.var1.
-        */
-       name = var + 6;
-       namelen = ep - name;
-       for (fn = ll_user_merge; fn; fn = fn->next)
-               if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
-                       break;
-       if (!fn) {
-               fn = xcalloc(1, sizeof(struct ll_merge_driver));
-               fn->name = xmemdupz(name, namelen);
-               fn->fn = ll_ext_merge;
-               *ll_user_merge_tail = fn;
-               ll_user_merge_tail = &(fn->next);
-       }
-
-       ep++;
-
-       if (!strcmp("name", ep)) {
-               if (!value)
-                       return config_error_nonbool(var);
-               fn->description = strdup(value);
-               return 0;
-       }
-
-       if (!strcmp("driver", ep)) {
-               if (!value)
-                       return config_error_nonbool(var);
-               /*
-                * merge.<name>.driver specifies the command line:
-                *
-                *      command-line
-                *
-                * The command-line will be interpolated with the following
-                * tokens and is given to the shell:
-                *
-                *    %O - temporary file name for the merge base.
-                *    %A - temporary file name for our version.
-                *    %B - temporary file name for the other branches' version.
-                *
-                * The external merge driver should write the results in the
-                * file named by %A, and signal that it has done with zero exit
-                * status.
-                */
-               fn->cmdline = strdup(value);
-               return 0;
-       }
-
-       if (!strcmp("recursive", ep)) {
-               if (!value)
-                       return config_error_nonbool(var);
-               fn->recursive = strdup(value);
-               return 0;
-       }
-
-       return 0;
-}
-
-static void initialize_ll_merge(void)
-{
-       if (ll_user_merge_tail)
-               return;
-       ll_user_merge_tail = &ll_user_merge;
-       git_config(read_merge_config);
-}
-
-static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
-{
-       struct ll_merge_driver *fn;
-       const char *name;
-       int i;
-
-       initialize_ll_merge();
-
-       if (ATTR_TRUE(merge_attr))
-               return &ll_merge_drv[LL_TEXT_MERGE];
-       else if (ATTR_FALSE(merge_attr))
-               return &ll_merge_drv[LL_BINARY_MERGE];
-       else if (ATTR_UNSET(merge_attr)) {
-               if (!default_ll_merge)
-                       return &ll_merge_drv[LL_TEXT_MERGE];
-               else
-                       name = default_ll_merge;
-       }
-       else
-               name = merge_attr;
-
-       for (fn = ll_user_merge; fn; fn = fn->next)
-               if (!strcmp(fn->name, name))
-                       return fn;
-
-       for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
-               if (!strcmp(ll_merge_drv[i].name, name))
-                       return &ll_merge_drv[i];
-
-       /* default to the 3-way */
-       return &ll_merge_drv[LL_TEXT_MERGE];
-}
-
-static const char *git_path_check_merge(const char *path)
-{
-       static struct git_attr_check attr_merge_check;
-
-       if (!attr_merge_check.attr)
-               attr_merge_check.attr = git_attr("merge", 5);
-
-       if (git_checkattr(path, 1, &attr_merge_check))
-               return NULL;
-       return attr_merge_check.value;
-}
-
-static int ll_merge(mmbuffer_t *result_buf,
-                   struct diff_filespec *o,
-                   struct diff_filespec *a,
-                   struct diff_filespec *b,
-                   const char *branch1,
-                   const char *branch2)
+static int merge_3way(mmbuffer_t *result_buf,
+                     struct diff_filespec *o,
+                     struct diff_filespec *a,
+                     struct diff_filespec *b,
+                     const char *branch1,
+                     const char *branch2)
 {
        mmfile_t orig, src1, src2;
        char *name1, *name2;
        int merge_status;
-       const char *ll_driver_name;
-       const struct ll_merge_driver *driver;
 
        name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
        name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
@@ -983,14 +635,9 @@ static int ll_merge(mmbuffer_t *result_buf,
        fill_mm(a->sha1, &src1);
        fill_mm(b->sha1, &src2);
 
-       ll_driver_name = git_path_check_merge(a->path);
-       driver = find_ll_merge_driver(ll_driver_name);
-
-       if (index_only && driver->recursive)
-               driver = find_ll_merge_driver(driver->recursive);
-       merge_status = driver->fn(driver, a->path,
-                                 &orig, &src1, name1, &src2, name2,
-                                 result_buf);
+       merge_status = ll_merge(result_buf, a->path, &orig,
+                               &src1, name1, &src2, name2,
+                               index_only);
 
        free(name1);
        free(name2);
@@ -1031,8 +678,8 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
                        mmbuffer_t result_buf;
                        int merge_status;
 
-                       merge_status = ll_merge(&result_buf, o, a, b,
-                                               branch1, branch2);
+                       merge_status = merge_3way(&result_buf, o, a, b,
+                                                 branch1, branch2);
 
                        if ((merge_status < 0) || !result_buf.ptr)
                                die("Failed to execute internal merge");
index 28c36fdcd1658968ff7c3e2f1d6ba6f364f99592..8dd959fe1c74507023f8e82c7f4682c1588ebac6 100644 (file)
@@ -50,12 +50,15 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
                size_t len = strlen(editor);
                int i = 0;
                const char *args[6];
+               struct strbuf arg0;
 
+               strbuf_init(&arg0, 0);
                if (strcspn(editor, "$ \t'") != len) {
                        /* there are specials */
+                       strbuf_addf(&arg0, "%s \"$@\"", editor);
                        args[i++] = "sh";
                        args[i++] = "-c";
-                       args[i++] = "$0 \"$@\"";
+                       args[i++] = arg0.buf;
                }
                args[i++] = editor;
                args[i++] = path;
@@ -63,6 +66,7 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
 
                if (run_command_v_opt_cd_env(args, 0, NULL, env))
                        die("There was a problem with the editor %s.", editor);
+               strbuf_release(&arg0);
        }
 
        if (!buffer)
diff --git a/cache.h b/cache.h
index b519b7a0c374f71c57360895a05cd38b22b70964..2a1e7ec6b2bf712af80813936b5aed434d02090e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -544,6 +544,9 @@ extern void *read_object_with_reference(const unsigned char *sha1,
                                        unsigned long *size,
                                        unsigned char *sha1_ret);
 
+extern struct object *peel_to_type(const char *name, int namelen,
+                                  struct object *o, enum object_type);
+
 enum date_mode {
        DATE_NORMAL = 0,
        DATE_RELATIVE,
index 848c067b57398154de6dc5287648b4103841217a..5046f699349aa87bedbc79a997dfcb9ea54e7618 100755 (executable)
@@ -83,17 +83,17 @@ __git_ps1 ()
                elif [ -f "$g/.dotest-merge/interactive" ]
                then
                        r="|REBASE-i"
-                       b="$(cat $g/.dotest-merge/head-name)"
+                       b="$(cat "$g/.dotest-merge/head-name")"
                elif [ -d "$g/.dotest-merge" ]
                then
                        r="|REBASE-m"
-                       b="$(cat $g/.dotest-merge/head-name)"
+                       b="$(cat "$g/.dotest-merge/head-name")"
                elif [ -f "$g/MERGE_HEAD" ]
                then
                        r="|MERGING"
                        b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
-                       if [ -f $g/BISECT_LOG ]
+                       if [ -f "$g/BISECT_LOG" ]
                        then
                                r="|BISECTING"
                        fi
@@ -101,7 +101,7 @@ __git_ps1 ()
                        then
                                if ! b="$(git describe --exact-match HEAD 2>/dev/null)"
                                then
-                                       b="$(cut -c1-7 $g/HEAD)..."
+                                       b="$(cut -c1-7 "$g/HEAD")..."
                                fi
                        fi
                fi
@@ -121,13 +121,21 @@ __gitcomp ()
        if [ $# -gt 2 ]; then
                cur="$3"
        fi
-       for c in $1; do
-               case "$c$4" in
-               --*=*) all="$all$c$4$s" ;;
-               *.)    all="$all$c$4$s" ;;
-               *)     all="$all$c$4 $s" ;;
-               esac
-       done
+       case "$cur" in
+       --*=)
+               COMPREPLY=()
+               return
+               ;;
+       *)
+               for c in $1; do
+                       case "$c$4" in
+                       --*=*) all="$all$c$4$s" ;;
+                       *.)    all="$all$c$4$s" ;;
+                       *)     all="$all$c$4 $s" ;;
+                       esac
+               done
+               ;;
+       esac
        IFS=$s
        COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur"))
        return
@@ -384,7 +392,6 @@ __git_commands ()
                show-index)       : plumbing;;
                ssh-*)            : transport;;
                stripspace)       : plumbing;;
-               svn)              : import export;;
                symbolic-ref)     : plumbing;;
                tar-tree)         : deprecated;;
                unpack-file)      : plumbing;;
@@ -428,6 +435,22 @@ __git_aliased_command ()
        done
 }
 
+__git_find_subcommand ()
+{
+       local word subcommand c=1
+
+       while [ $c -lt $COMP_CWORD ]; do
+               word="${COMP_WORDS[c]}"
+               for subcommand in $1; do
+                       if [ "$subcommand" = "$word" ]; then
+                               echo "$subcommand"
+                               return
+                       fi
+               done
+               c=$((++c))
+       done
+}
+
 __git_whitespacelist="nowarn warn error error-all strip"
 
 _git_am ()
@@ -485,24 +508,14 @@ _git_add ()
 
 _git_bisect ()
 {
-       local i c=1 command
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
-               case "$i" in
-               start|bad|good|reset|visualize|replay|log)
-                       command="$i"
-                       break
-                       ;;
-               esac
-               c=$((++c))
-       done
-
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
-               __gitcomp "start bad good reset visualize replay log"
+       local subcommands="start bad good reset visualize replay log"
+       local subcommand="$(__git_find_subcommand "$subcommands")"
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
                return
        fi
 
-       case "$command" in
+       case "$subcommand" in
        bad|good|reset)
                __gitcomp "$(__git_refs)"
                ;;
@@ -836,8 +849,8 @@ _git_push ()
 
 _git_rebase ()
 {
-       local cur="${COMP_WORDS[COMP_CWORD]}"
-       if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then
+       local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+       if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then
                __gitcomp "--continue --skip --abort"
                return
        fi
@@ -956,7 +969,6 @@ _git_config ()
                core.sharedRepository
                core.warnAmbiguousRefs
                core.compression
-               core.legacyHeaders
                core.packedGitWindowSize
                core.packedGitLimit
                clean.requireForce
@@ -1033,21 +1045,13 @@ _git_config ()
 
 _git_remote ()
 {
-       local i c=1 command
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
-               case "$i" in
-               add|rm|show|prune|update) command="$i"; break ;;
-               esac
-               c=$((++c))
-       done
-
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
-               __gitcomp "add rm show prune update"
+       local subcommands="add rm show prune update"
+       local subcommand="$(__git_find_subcommand "$subcommands")"
+       if [ -z "$subcommand" ]; then
                return
        fi
 
-       case "$command" in
+       case "$subcommand" in
        rm|show|prune)
                __gitcomp "$(__git_remotes)"
                ;;
@@ -1121,34 +1125,107 @@ _git_show ()
 
 _git_stash ()
 {
-       __gitcomp 'list show apply clear'
+       local subcommands='save list show apply clear drop pop create'
+       if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
+               __gitcomp "$subcommands"
+       fi
 }
 
 _git_submodule ()
 {
-       local i c=1 command
-       while [ $c -lt $COMP_CWORD ]; do
-               i="${COMP_WORDS[c]}"
-               case "$i" in
-               add|status|init|update) command="$i"; break ;;
-               esac
-               c=$((++c))
-       done
-
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+       local subcommands="add status init update"
+       if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
                local cur="${COMP_WORDS[COMP_CWORD]}"
                case "$cur" in
                --*)
                        __gitcomp "--quiet --cached"
                        ;;
                *)
-                       __gitcomp "add status init update"
+                       __gitcomp "$subcommands"
                        ;;
                esac
                return
        fi
 }
 
+_git_svn ()
+{
+       local subcommands="
+               init fetch clone rebase dcommit log find-rev
+               set-tree commit-diff info create-ignore propget
+               proplist show-ignore show-externals
+               "
+       local subcommand="$(__git_find_subcommand "$subcommands")"
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
+       else
+               local remote_opts="--username= --config-dir= --no-auth-cache"
+               local fc_opts="
+                       --follow-parent --authors-file= --repack=
+                       --no-metadata --use-svm-props --use-svnsync-props
+                       --log-window-size= --no-checkout --quiet
+                       --repack-flags --user-log-author $remote_opts
+                       "
+               local init_opts="
+                       --template= --shared= --trunk= --tags=
+                       --branches= --stdlayout --minimize-url
+                       --no-metadata --use-svm-props --use-svnsync-props
+                       --rewrite-root= $remote_opts
+                       "
+               local cmt_opts="
+                       --edit --rmdir --find-copies-harder --copy-similarity=
+                       "
+
+               local cur="${COMP_WORDS[COMP_CWORD]}"
+               case "$subcommand,$cur" in
+               fetch,--*)
+                       __gitcomp "--revision= --fetch-all $fc_opts"
+                       ;;
+               clone,--*)
+                       __gitcomp "--revision= $fc_opts $init_opts"
+                       ;;
+               init,--*)
+                       __gitcomp "$init_opts"
+                       ;;
+               dcommit,--*)
+                       __gitcomp "
+                               --merge --strategy= --verbose --dry-run
+                               --fetch-all --no-rebase $cmt_opts $fc_opts
+                               "
+                       ;;
+               set-tree,--*)
+                       __gitcomp "--stdin $cmt_opts $fc_opts"
+                       ;;
+               create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
+               show-externals,--*)
+                       __gitcomp "--revision="
+                       ;;
+               log,--*)
+                       __gitcomp "
+                               --limit= --revision= --verbose --incremental
+                               --oneline --show-commit --non-recursive
+                               --authors-file=
+                               "
+                       ;;
+               rebase,--*)
+                       __gitcomp "
+                               --merge --verbose --strategy= --local
+                               --fetch-all $fc_opts
+                               "
+                       ;;
+               commit-diff,--*)
+                       __gitcomp "--message= --file= --revision= $cmt_opts"
+                       ;;
+               info,--*)
+                       __gitcomp "--url"
+                       ;;
+               *)
+                       COMPREPLY=()
+                       ;;
+               esac
+       fi
+}
+
 _git_tag ()
 {
        local i c=1 f=0
@@ -1198,15 +1275,18 @@ _git ()
                c=$((++c))
        done
 
-       if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
+       if [ -z "$command" ]; then
                case "${COMP_WORDS[COMP_CWORD]}" in
                --*=*) COMPREPLY=() ;;
                --*)   __gitcomp "
+                       --paginate
                        --no-pager
                        --git-dir=
                        --bare
                        --version
                        --exec-path
+                       --work-tree=
+                       --help
                        "
                        ;;
                *)     __gitcomp "$(__git_commands) $(__git_aliases)" ;;
@@ -1250,6 +1330,7 @@ _git ()
        show-branch) _git_log ;;
        stash)       _git_stash ;;
        submodule)   _git_submodule ;;
+       svn)         _git_svn ;;
        tag)         _git_tag ;;
        whatchanged) _git_log ;;
        *)           COMPREPLY=() ;;
@@ -1300,6 +1381,7 @@ complete -o default -o nospace -F _git_shortlog git-shortlog
 complete -o default -o nospace -F _git_show git-show
 complete -o default -o nospace -F _git_stash git-stash
 complete -o default -o nospace -F _git_submodule git-submodule
+complete -o default -o nospace -F _git_svn git-svn
 complete -o default -o nospace -F _git_log git-show-branch
 complete -o default -o nospace -F _git_tag git-tag
 complete -o default -o nospace -F _git_log git-whatchanged
index 452c5e7e01e168ec4fcdc5bb87caac3f326f95ea..ff66af3ba8348ff86b25c91a1875b93f885a048c 100755 (executable)
@@ -18,8 +18,7 @@ original <branch> and remove the .dotest working files, use the command
 git rebase --abort instead.
 
 Note that if <branch> is not specified on the command line, the
-currently checked out branch is used.  You must be in the top
-directory of your project to start (or continue) a rebase.
+currently checked out branch is used.
 
 Example:       git-rebase master~1 topic
 
index 1195569529401fd1c62414c8683a6b577721c1c7..d8b38c9a47c0b77dd55781a66e42e4dec26fd4b7 100755 (executable)
@@ -522,7 +522,8 @@ sub cmd_dcommit {
 }
 
 sub cmd_find_rev {
-       my $revision_or_hash = shift;
+       my $revision_or_hash = shift or die "SVN or git revision required ",
+                                           "as a command-line argument\n";
        my $result;
        if ($revision_or_hash =~ /^r\d+$/) {
                my $head = shift;
diff --git a/ll-merge.c b/ll-merge.c
new file mode 100644 (file)
index 0000000..5ae7433
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Low level 3-way in-core file merge.
+ *
+ * Copyright (c) 2007 Junio C Hamano
+ */
+
+#include "cache.h"
+#include "attr.h"
+#include "xdiff-interface.h"
+#include "run-command.h"
+#include "interpolate.h"
+#include "ll-merge.h"
+
+struct ll_merge_driver;
+
+typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
+                          mmbuffer_t *result,
+                          const char *path,
+                          mmfile_t *orig,
+                          mmfile_t *src1, const char *name1,
+                          mmfile_t *src2, const char *name2,
+                          int virtual_ancestor);
+
+struct ll_merge_driver {
+       const char *name;
+       const char *description;
+       ll_merge_fn fn;
+       const char *recursive;
+       struct ll_merge_driver *next;
+       char *cmdline;
+};
+
+/*
+ * Built-in low-levels
+ */
+static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
+                          mmbuffer_t *result,
+                          const char *path_unused,
+                          mmfile_t *orig,
+                          mmfile_t *src1, const char *name1,
+                          mmfile_t *src2, const char *name2,
+                          int virtual_ancestor)
+{
+       /*
+        * 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;
+
+       result->ptr = stolen->ptr;
+       result->size = stolen->size;
+       stolen->ptr = NULL;
+       return 1;
+}
+
+static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
+                       mmbuffer_t *result,
+                       const char *path_unused,
+                       mmfile_t *orig,
+                       mmfile_t *src1, const char *name1,
+                       mmfile_t *src2, const char *name2,
+                       int virtual_ancestor)
+{
+       xpparam_t xpp;
+
+       if (buffer_is_binary(orig->ptr, orig->size) ||
+           buffer_is_binary(src1->ptr, src1->size) ||
+           buffer_is_binary(src2->ptr, src2->size)) {
+               warning("Cannot merge binary files: %s vs. %s\n",
+                       name1, name2);
+               return ll_binary_merge(drv_unused, result,
+                                      path_unused,
+                                      orig, src1, name1,
+                                      src2, name2,
+                                      virtual_ancestor);
+       }
+
+       memset(&xpp, 0, sizeof(xpp));
+       return xdl_merge(orig,
+                        src1, name1,
+                        src2, name2,
+                        &xpp, XDL_MERGE_ZEALOUS,
+                        result);
+}
+
+static int ll_union_merge(const struct ll_merge_driver *drv_unused,
+                         mmbuffer_t *result,
+                         const char *path_unused,
+                         mmfile_t *orig,
+                         mmfile_t *src1, const char *name1,
+                         mmfile_t *src2, const char *name2,
+                         int virtual_ancestor)
+{
+       char *src, *dst;
+       long size;
+       const int marker_size = 7;
+
+       int status = ll_xdl_merge(drv_unused, result, path_unused,
+                                 orig, src1, NULL, src2, NULL,
+                                 virtual_ancestor);
+       if (status <= 0)
+               return status;
+       size = result->size;
+       src = dst = result->ptr;
+       while (size) {
+               char ch;
+               if ((marker_size < size) &&
+                   (*src == '<' || *src == '=' || *src == '>')) {
+                       int i;
+                       ch = *src;
+                       for (i = 0; i < marker_size; i++)
+                               if (src[i] != ch)
+                                       goto not_a_marker;
+                       if (src[marker_size] != '\n')
+                               goto not_a_marker;
+                       src += marker_size + 1;
+                       size -= marker_size + 1;
+                       continue;
+               }
+       not_a_marker:
+               do {
+                       ch = *src++;
+                       *dst++ = ch;
+                       size--;
+               } while (ch != '\n' && size);
+       }
+       result->size = dst - result->ptr;
+       return 0;
+}
+
+#define LL_BINARY_MERGE 0
+#define LL_TEXT_MERGE 1
+#define LL_UNION_MERGE 2
+static struct ll_merge_driver ll_merge_drv[] = {
+       { "binary", "built-in binary merge", ll_binary_merge },
+       { "text", "built-in 3-way text merge", ll_xdl_merge },
+       { "union", "built-in union merge", ll_union_merge },
+};
+
+static void create_temp(mmfile_t *src, char *path)
+{
+       int fd;
+
+       strcpy(path, ".merge_file_XXXXXX");
+       fd = xmkstemp(path);
+       if (write_in_full(fd, src->ptr, src->size) != src->size)
+               die("unable to write temp-file");
+       close(fd);
+}
+
+/*
+ * User defined low-level merge driver support.
+ */
+static int ll_ext_merge(const struct ll_merge_driver *fn,
+                       mmbuffer_t *result,
+                       const char *path,
+                       mmfile_t *orig,
+                       mmfile_t *src1, const char *name1,
+                       mmfile_t *src2, const char *name2,
+                       int virtual_ancestor)
+{
+       char temp[3][50];
+       char cmdbuf[2048];
+       struct interp table[] = {
+               { "%O" },
+               { "%A" },
+               { "%B" },
+       };
+       struct child_process child;
+       const char *args[20];
+       int status, fd, i;
+       struct stat st;
+
+       if (fn->cmdline == NULL)
+               die("custom merge driver %s lacks command line.", fn->name);
+
+       result->ptr = NULL;
+       result->size = 0;
+       create_temp(orig, temp[0]);
+       create_temp(src1, temp[1]);
+       create_temp(src2, temp[2]);
+
+       interp_set_entry(table, 0, temp[0]);
+       interp_set_entry(table, 1, temp[1]);
+       interp_set_entry(table, 2, temp[2]);
+
+       interpolate(cmdbuf, sizeof(cmdbuf), fn->cmdline, table, 3);
+
+       memset(&child, 0, sizeof(child));
+       child.argv = args;
+       args[0] = "sh";
+       args[1] = "-c";
+       args[2] = cmdbuf;
+       args[3] = NULL;
+
+       status = run_command(&child);
+       if (status < -ERR_RUN_COMMAND_FORK)
+               ; /* failure in run-command */
+       else
+               status = -status;
+       fd = open(temp[1], O_RDONLY);
+       if (fd < 0)
+               goto bad;
+       if (fstat(fd, &st))
+               goto close_bad;
+       result->size = st.st_size;
+       result->ptr = xmalloc(result->size + 1);
+       if (read_in_full(fd, result->ptr, result->size) != result->size) {
+               free(result->ptr);
+               result->ptr = NULL;
+               result->size = 0;
+       }
+ close_bad:
+       close(fd);
+ bad:
+       for (i = 0; i < 3; i++)
+               unlink(temp[i]);
+       return status;
+}
+
+/*
+ * merge.default and merge.driver configuration items
+ */
+static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail;
+static const char *default_ll_merge;
+
+static int read_merge_config(const char *var, const char *value)
+{
+       struct ll_merge_driver *fn;
+       const char *ep, *name;
+       int namelen;
+
+       if (!strcmp(var, "merge.default")) {
+               if (value)
+                       default_ll_merge = strdup(value);
+               return 0;
+       }
+
+       /*
+        * We are not interested in anything but "merge.<name>.variable";
+        * especially, we do not want to look at variables such as
+        * "merge.summary", "merge.tool", and "merge.verbosity".
+        */
+       if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5)
+               return 0;
+
+       /*
+        * Find existing one as we might be processing merge.<name>.var2
+        * after seeing merge.<name>.var1.
+        */
+       name = var + 6;
+       namelen = ep - name;
+       for (fn = ll_user_merge; fn; fn = fn->next)
+               if (!strncmp(fn->name, name, namelen) && !fn->name[namelen])
+                       break;
+       if (!fn) {
+               fn = xcalloc(1, sizeof(struct ll_merge_driver));
+               fn->name = xmemdupz(name, namelen);
+               fn->fn = ll_ext_merge;
+               *ll_user_merge_tail = fn;
+               ll_user_merge_tail = &(fn->next);
+       }
+
+       ep++;
+
+       if (!strcmp("name", ep)) {
+               if (!value)
+                       return error("%s: lacks value", var);
+               fn->description = strdup(value);
+               return 0;
+       }
+
+       if (!strcmp("driver", ep)) {
+               if (!value)
+                       return error("%s: lacks value", var);
+               /*
+                * merge.<name>.driver specifies the command line:
+                *
+                *      command-line
+                *
+                * The command-line will be interpolated with the following
+                * tokens and is given to the shell:
+                *
+                *    %O - temporary file name for the merge base.
+                *    %A - temporary file name for our version.
+                *    %B - temporary file name for the other branches' version.
+                *
+                * The external merge driver should write the results in the
+                * file named by %A, and signal that it has done with zero exit
+                * status.
+                */
+               fn->cmdline = strdup(value);
+               return 0;
+       }
+
+       if (!strcmp("recursive", ep)) {
+               if (!value)
+                       return error("%s: lacks value", var);
+               fn->recursive = strdup(value);
+               return 0;
+       }
+
+       return 0;
+}
+
+static void initialize_ll_merge(void)
+{
+       if (ll_user_merge_tail)
+               return;
+       ll_user_merge_tail = &ll_user_merge;
+       git_config(read_merge_config);
+}
+
+static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr)
+{
+       struct ll_merge_driver *fn;
+       const char *name;
+       int i;
+
+       initialize_ll_merge();
+
+       if (ATTR_TRUE(merge_attr))
+               return &ll_merge_drv[LL_TEXT_MERGE];
+       else if (ATTR_FALSE(merge_attr))
+               return &ll_merge_drv[LL_BINARY_MERGE];
+       else if (ATTR_UNSET(merge_attr)) {
+               if (!default_ll_merge)
+                       return &ll_merge_drv[LL_TEXT_MERGE];
+               else
+                       name = default_ll_merge;
+       }
+       else
+               name = merge_attr;
+
+       for (fn = ll_user_merge; fn; fn = fn->next)
+               if (!strcmp(fn->name, name))
+                       return fn;
+
+       for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++)
+               if (!strcmp(ll_merge_drv[i].name, name))
+                       return &ll_merge_drv[i];
+
+       /* default to the 3-way */
+       return &ll_merge_drv[LL_TEXT_MERGE];
+}
+
+static const char *git_path_check_merge(const char *path)
+{
+       static struct git_attr_check attr_merge_check;
+
+       if (!attr_merge_check.attr)
+               attr_merge_check.attr = git_attr("merge", 5);
+
+       if (git_checkattr(path, 1, &attr_merge_check))
+               return NULL;
+       return attr_merge_check.value;
+}
+
+int ll_merge(mmbuffer_t *result_buf,
+            const char *path,
+            mmfile_t *ancestor,
+            mmfile_t *ours, const char *our_label,
+            mmfile_t *theirs, const char *their_label,
+            int virtual_ancestor)
+{
+       const char *ll_driver_name;
+       const struct ll_merge_driver *driver;
+
+       ll_driver_name = git_path_check_merge(path);
+       driver = find_ll_merge_driver(ll_driver_name);
+
+       if (virtual_ancestor && driver->recursive)
+               driver = find_ll_merge_driver(driver->recursive);
+       return driver->fn(driver, result_buf, path,
+                         ancestor,
+                         ours, our_label,
+                         theirs, their_label, virtual_ancestor);
+}
diff --git a/ll-merge.h b/ll-merge.h
new file mode 100644 (file)
index 0000000..5388422
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Low level 3-way in-core file merge.
+ */
+
+#ifndef LL_MERGE_H
+#define LL_MERGE_H
+
+int ll_merge(mmbuffer_t *result_buf,
+            const char *path,
+            mmfile_t *ancestor,
+            mmfile_t *ours, const char *our_label,
+            mmfile_t *theirs, const char *their_label,
+            int virtual_ancestor);
+
+#endif
index 8358ba2069746943fcf45d35222d95fab7a8231d..8b6c76f68ed1433caddfa8aab6132db667197433 100644 (file)
@@ -422,6 +422,37 @@ static int get_nth_ancestor(const char *name, int len,
        return 0;
 }
 
+struct object *peel_to_type(const char *name, int namelen,
+                           struct object *o, enum object_type expected_type)
+{
+       if (name && !namelen)
+               namelen = strlen(name);
+       if (!o) {
+               unsigned char sha1[20];
+               if (get_sha1_1(name, namelen, sha1))
+                       return NULL;
+               o = parse_object(sha1);
+       }
+       while (1) {
+               if (!o || (!o->parsed && !parse_object(o->sha1)))
+                       return NULL;
+               if (o->type == expected_type)
+                       return o;
+               if (o->type == OBJ_TAG)
+                       o = ((struct tag*) o)->tagged;
+               else if (o->type == OBJ_COMMIT)
+                       o = &(((struct commit *) o)->tree->object);
+               else {
+                       if (name)
+                               error("%.*s: expected %s type, but the object "
+                                     "dereferences to %s type",
+                                     namelen, name, typename(expected_type),
+                                     typename(o->type));
+                       return NULL;
+               }
+       }
+}
+
 static int peel_onion(const char *name, int len, unsigned char *sha1)
 {
        unsigned char outer[20];
@@ -473,32 +504,17 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                hashcpy(sha1, o->sha1);
        }
        else {
-               /* At this point, the syntax look correct, so
+               /*
+                * At this point, the syntax look correct, so
                 * if we do not get the needed object, we should
                 * barf.
                 */
-
-               while (1) {
-                       if (!o || (!o->parsed && !parse_object(o->sha1)))
-                               return -1;
-                       if (o->type == expected_type) {
-                               hashcpy(sha1, o->sha1);
-                               return 0;
-                       }
-                       if (o->type == OBJ_TAG)
-                               o = ((struct tag*) o)->tagged;
-                       else if (o->type == OBJ_COMMIT)
-                               o = &(((struct commit *) o)->tree->object);
-                       else
-                               return error("%.*s: expected %s type, but the object dereferences to %s type",
-                                            len, name, typename(expected_type),
-                                            typename(o->type));
-                       if (!o)
-                               return -1;
-                       if (!o->parsed)
-                               if (!parse_object(o->sha1))
-                                       return -1;
+               o = peel_to_type(name, len, o, expected_type);
+               if (o) {
+                       hashcpy(sha1, o->sha1);
+                       return 0;
                }
+               return -1;
        }
        return 0;
 }
index cb860296edfab2d117fc8b62505df00a51baed02..8fc39d77cec6168dae930beef785597dace24aa3 100755 (executable)
@@ -5,7 +5,9 @@ test_description='blob conversion via gitattributes'
 . ./test-lib.sh
 
 cat <<\EOF >rot13.sh
-tr '[a-zA-Z]' '[n-za-mN-ZA-M]'
+tr \
+  'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
+  'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
 EOF
 chmod +x rot13.sh
 
index c1cec553060a2fd6a4d27191a866b3bf53ba3335..6a74b3acfda6774d7996cf5787e9858654357328 100755 (executable)
@@ -89,6 +89,33 @@ do
        '
 done
 
+test_expect_success 'editor with a space' '
+
+       if echo "echo space > \"\$1\"" > "e space.sh"
+       then
+               chmod a+x "e space.sh" &&
+               GIT_EDITOR="./e\ space.sh" git commit --amend &&
+               test space = "$(git show -s --pretty=format:%s)"
+       else
+               say "Skipping; FS does not support spaces in filenames"
+       fi
+
+'
+
+unset GIT_EDITOR
+test_expect_success 'core.editor with a space' '
+
+       if test -f "e space.sh"
+       then
+               git config core.editor \"./e\ space.sh\" &&
+               git commit --amend &&
+               test space = "$(git show -s --pretty=format:%s)"
+       else
+               say "Skipping; FS does not support spaces in filenames"
+       fi
+
+'
+
 TERM="$OLD_TERM"
 
 test_done