Merge branch 'jl/diff-submodule-ignore'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Jan 2010 06:53:13 +0000 (22:53 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Jan 2010 06:53:13 +0000 (22:53 -0800)
* jl/diff-submodule-ignore:
Teach diff --submodule that modified submodule directory is dirty
git diff: Don't test submodule dirtiness with --ignore-submodules
Make ce_uptodate() trustworthy again

60 files changed:
.gitignore
Documentation/config.txt
Documentation/git-ls-files.txt
Documentation/git-merge.txt
Documentation/git-push.txt
Documentation/git-shortlog.txt
Makefile
builtin-commit-tree.c
builtin-read-tree.c
cache.h
connect.c
diff-lib.c
diff.c
diff.h
git-am.sh
git-filter-branch.sh
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/choose_repository.tcl
git-gui/lib/diff.tcl
git-gui/lib/index.tcl
git-instaweb.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
gitweb/gitweb.js
grep.c
t/diff-lib.sh
t/t0020-crlf.sh
t/t0022-crlf-rename.sh
t/t1000-read-tree-m-3way.sh
t/t1001-read-tree-m-2way.sh
t/t1002-read-tree-m-u-2way.sh
t/t1012-read-tree-df.sh [new file with mode: 0755]
t/t1506-rev-parse-upstream.sh [deleted file]
t/t1507-rev-parse-upstream.sh [new file with mode: 0755]
t/t3100-ls-tree-restrict.sh
t/t3101-ls-tree-dirname.sh
t/t3400-rebase.sh
t/t3408-rebase-multi-line.sh
t/t4006-diff-mode.sh
t/t4012-diff-binary.sh
t/t4014-format-patch.sh
t/t4125-apply-ws-fuzz.sh
t/t4150-am.sh
t/t4202-log.sh
t/t5524-pull-msg.sh [new file with mode: 0755]
t/t5541-http-push.sh
t/t6012-rev-list-simplify.sh
t/t6023-merge-file.sh
t/t6033-merge-crlf.sh
t/t6035-merge-dir-to-symlink.sh
t/t7500-commit.sh
t/t9001-send-email.sh
t/t9400-git-cvsserver-server.sh
t/t9401-git-cvsserver-crlf.sh
t/test-lib.sh
tree-walk.c
unpack-trees.c
unpack-trees.h
index 83cf1b7532f6cf35c1fefdedb7372062a10b468f..8df8f88bea10e02371a41ca20420ee1607814dc8 100644 (file)
 /test-match-trees
 /test-parse-options
 /test-path-utils
+/test-run-command
 /test-sha1
 /test-sigchain
 /common-cmds.h
index 8dcb19156623277a31992b5ee6818eca08bb9195..17901e244a832efde1f6264957c8f9e8c2bd448a 100644 (file)
@@ -716,7 +716,7 @@ color.ui::
        terminal. When more specific variables of color.* are set, they always
        take precedence over this setting. Defaults to false.
 
-commit.status
+commit.status::
        A boolean to enable/disable inclusion of status information in the
        commit message template when using an editor to prepare the commit
        message.  Defaults to true.
index af409bf3b2fed15c332ec26656785b3b3d14e991..3521637b582687978c088dc463e8784817a92217 100644 (file)
@@ -141,7 +141,7 @@ OPTIONS
 
 Output
 ------
-show files just outputs the filename unless '--stage' is specified in
+'git ls-files' just outputs the filenames unless '--stage' is specified in
 which case it outputs:
 
         [<tag> ]<mode> <object> <stage> <file>
index d4ef0d0ce2a3c75f964465e25106f9ab3ac53d89..9c9618cead5ae73a754ce741dfd423a7bd2298ca 100644 (file)
@@ -15,8 +15,32 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Merges the history specified by <commit> into HEAD, optionally using a
-specific merge strategy.
+Incorporates changes from the named commits (since the time their
+histories diverged from the current branch) into the current
+branch.  This command is used by 'git pull' to incorporate changes
+from another repository and can be used by hand to merge changes
+from one branch into another.
+
+Assume the following history exists and the current branch is
+"`master`":
+
+------------
+         A---B---C topic
+        /
+    D---E---F---G master
+------------
+
+Then "`git merge topic`" will replay the changes made on the
+`topic` branch since it diverged from `master` (i.e., `E`) until
+its current commit (`C`) on top of `master`, and record the result
+in a new commit along with the names of the two parent commits and
+a log message from the user describing the changes.
+
+------------
+         A---B---C topic
+        /         \
+    D---E---F---G---H master
+------------
 
 The second syntax (<msg> `HEAD` <commit>...) is supported for
 historical reasons.  Do not use it from the command line or in
@@ -47,88 +71,73 @@ include::merge-options.txt[]
        You need at least one <commit>.  Specifying more than one
        <commit> obviously means you are trying an Octopus.
 
-include::merge-strategies.txt[]
 
+PRE-MERGE CHECKS
+----------------
 
-If you tried a merge which resulted in complex conflicts and
-want to start over, you can recover with 'git reset'.
+Before applying outside changes, you should get your own work in
+good shape and committed locally, so it will not be clobbered if
+there are conflicts.  See also linkgit:git-stash[1].
+'git pull' and 'git merge' will stop without doing anything when
+local uncommitted changes overlap with files that 'git pull'/'git
+merge' may need to update.
 
-CONFIGURATION
--------------
-include::merge-config.txt[]
+To avoid recording unrelated changes in the merge commit,
+'git pull' and 'git merge' will also abort if there are any changes
+registered in the index relative to the `HEAD` commit.  (One
+exception is when the changed index entries are in the state that
+would result from the merge already.)
 
-branch.<name>.mergeoptions::
-       Sets default options for merging into branch <name>. The syntax and
-       supported options are the same as those of 'git merge', but option
-       values containing whitespace characters are currently not supported.
+If all named commits are already ancestors of `HEAD`, 'git merge'
+will exit early with the message "Already up-to-date."
 
-HOW MERGE WORKS
----------------
-
-A merge is always between the current `HEAD` and one or more
-commits (usually, branch head or tag), and the index file must
-match the tree of `HEAD` commit (i.e. the contents of the last commit)
-when it starts out.  In other words, `git diff --cached HEAD` must
-report no changes.  (One exception is when the changed index
-entries are already in the same state that would result from
-the merge anyway.)
-
-Three kinds of merge can happen:
-
-* The merged commit is already contained in `HEAD`. This is the
-  simplest case, called "Already up-to-date."
-
-* `HEAD` is already contained in the merged commit. This is the
-  most common case especially when invoked from 'git pull':
-  you are tracking an upstream repository, have committed no local
-  changes and now you want to update to a newer upstream revision.
-  Your `HEAD` (and the index) is updated to point at the merged
-  commit, without creating an extra merge commit.  This is
-  called "Fast-forward".
-
-* Both the merged commit and `HEAD` are independent and must be
-  tied together by a merge commit that has both of them as its parents.
-  The rest of this section describes this "True merge" case.
-
-The chosen merge strategy merges the two commits into a single
-new source tree.
-When things merge cleanly, this is what happens:
-
-1. The results are updated both in the index file and in your
-   working tree;
-2. Index file is written out as a tree;
-3. The tree gets committed; and
-4. The `HEAD` pointer gets advanced.
-
-Because of 2., we require that the original state of the index
-file matches exactly the current `HEAD` commit; otherwise we
-will write out your local changes already registered in your
-index file along with the merge result, which is not good.
-Because 1. involves only those paths differing between your
-branch and the branch you are merging
-(which is typically a fraction of the whole tree), you can
-have local modifications in your working tree as long as they do
-not overlap with what the merge updates.
-
-When there are conflicts, the following happens:
-
-1. `HEAD` stays the same.
-
-2. Cleanly merged paths are updated both in the index file and
-   in your working tree.
+FAST-FORWARD MERGE
+------------------
+
+Often the current branch head is an ancestor of the named commit.
+This is the most common case especially when invoked from 'git
+pull': you are tracking an upstream repository, you have committed
+no local changes, and now you want to update to a newer upstream
+revision.  In this case, a new commit is not needed to store the
+combined history; instead, the `HEAD` (along with the index) is
+updated to point at the named commit, without creating an extra
+merge commit.
+
+This behavior can be suppressed with the `--no-ff` option.
 
-3. For conflicting paths, the index file records up to three
-   versions; stage1 stores the version from the common ancestor,
-   stage2 from `HEAD`, and stage3 from the other branch (you
+TRUE MERGE
+----------
+
+Except in a fast-forward merge (see above), the branches to be
+merged must be tied together by a merge commit that has both of them
+as its parents.
+
+A merged version reconciling the changes from all branches to be
+merged is committed, and your `HEAD`, index, and working tree are
+updated to it.  It is possible to have modifications in the working
+tree as long as they do not overlap; the update will preserve them.
+
+When it is not obvious how to reconcile the changes, the following
+happens:
+
+1. The `HEAD` pointer stays the same.
+2. The `MERGE_HEAD` ref is set to point to the other branch head.
+3. Paths that merged cleanly are updated both in the index file and
+   in your working tree.
+4. For conflicting paths, the index file records up to three
+   versions: stage 1 stores the version from the common ancestor,
+   stage 2 from `HEAD`, and stage 3 from `MERGE_HEAD` (you
    can inspect the stages with `git ls-files -u`).  The working
    tree files contain the result of the "merge" program; i.e. 3-way
-   merge results with familiar conflict markers `<<< === >>>`.
-
-4. No other changes are done.  In particular, the local
+   merge results with familiar conflict markers `<<<` `===` `>>>`.
+5. No other changes are made.  In particular, the local
    modifications you had before you started merge will stay the
    same and the index entries for them stay as they were,
    i.e. matching `HEAD`.
 
+If you tried a merge which resulted in complex conflicts and
+want to start over, you can recover with `git reset --merge`.
+
 HOW CONFLICTS ARE PRESENTED
 ---------------------------
 
@@ -211,15 +220,17 @@ You can work through the conflict with a number of tools:
    mergetool which will work you through the merge.
 
  * Look at the diffs.  `git diff` will show a three-way diff,
-   highlighting changes from both the HEAD and their versions.
+   highlighting changes from both the `HEAD` and `MERGE_HEAD`
+   versions.
 
- * Look at the diffs on their own. `git log --merge -p <path>`
-   will show diffs first for the HEAD version and then
-   their version.
+ * Look at the diffs from each branch. `git log --merge -p <path>`
+   will show diffs first for the `HEAD` version and then the
+   `MERGE_HEAD` version.
 
  * Look at the originals.  `git show :1:filename` shows the
-   common ancestor, `git show :2:filename` shows the HEAD
-   version and `git show :3:filename` shows their version.
+   common ancestor, `git show :2:filename` shows the `HEAD`
+   version, and `git show :3:filename` shows the `MERGE_HEAD`
+   version.
 
 
 EXAMPLES
@@ -254,6 +265,17 @@ changes into a merge commit.  Small fixups like bumping
 release/version name would be acceptable.
 
 
+include::merge-strategies.txt[]
+
+CONFIGURATION
+-------------
+include::merge-config.txt[]
+
+branch.<name>.mergeoptions::
+       Sets default options for merging into branch <name>. The syntax and
+       supported options are the same as those of 'git merge', but option
+       values containing whitespace characters are currently not supported.
+
 SEE ALSO
 --------
 linkgit:git-fmt-merge-msg[1], linkgit:git-pull[1],
index c63932b16c5292b935d0ddc0b9f022b5275633b4..73a921ca0f224506a0a5587205495c352909db53 100644 (file)
@@ -122,13 +122,6 @@ nor in any Push line of the corresponding remotes file---see below).
        the name "origin" is used. For this latter case, this option
        can be used to override the name "origin". In other words,
        the difference between these two commands
-
--u::
---set-upstream::
-       For every branch that is up to date or successfully pushed, add
-       upstream (tracking) reference, used by argument-less
-       linkgit:git-pull[1] and other commands. For more information,
-       see 'branch.<name>.merge' in linkgit:git-config[1].
 +
 --------------------------
 git push public         #1
@@ -139,6 +132,13 @@ is that #1 always pushes to "public" whereas #2 pushes to "public"
 only if the current branch does not track a remote branch. This is
 useful if you write an alias or script around 'git push'.
 
+-u::
+--set-upstream::
+       For every branch that is up to date or successfully pushed, add
+       upstream (tracking) reference, used by argument-less
+       linkgit:git-pull[1] and other commands. For more information,
+       see 'branch.<name>.merge' in linkgit:git-config[1].
+
 --thin::
 --no-thin::
        These options are passed to 'git send-pack'.  Thin
index ecf9e2718c1e6c720ca1155607c192100cdf4663..dfd4d0c2233df09b64a10d1ad31a36afaa01ec7c 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 git log --pretty=short | 'git shortlog' [-h] [-n] [-s] [-e] [-w]
-git shortlog [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] [<committish>...]
+'git shortlog' [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] [<committish>...]
 
 DESCRIPTION
 -----------
index c0dbee2ae40823b4e8433891a704f94eaaf6d525..af08c8f452d24d4fdbed9c08dfb3a31e318d7672 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -994,6 +994,7 @@ ifeq ($(uname_S),Windows)
        COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
        EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
+       PTHREAD_LIBS =
        lib =
 ifndef DEBUG
        BASIC_CFLAGS += -GL -Os -MT
@@ -1036,11 +1037,12 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
-       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
+       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
                compat/win32/pthread.o
        EXTLIBS += -lws2_32
+       PTHREAD_LIBS =
        X = .exe
 ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        htmldir=doc/git/html/
index ddcb7a4bbbcd45dfd7437209f3187fff882e6e62..90dac349a3137405baaea63bfb6c798d2f8c92a3 100644 (file)
@@ -9,8 +9,6 @@
 #include "builtin.h"
 #include "utf8.h"
 
-#define BLOCKING (1ul << 14)
-
 /*
  * FIXME! Share the code with "write-tree.c"
  */
index 5fda9905fcfa5ccbc0f28578cd371314a8a7e5a7..8bdcab11389e8b4facab355f27f0e1926134b86f 100644 (file)
@@ -65,6 +65,34 @@ static int exclude_per_directory_cb(const struct option *opt, const char *arg,
        return 0;
 }
 
+static void debug_stage(const char *label, struct cache_entry *ce,
+                       struct unpack_trees_options *o)
+{
+       printf("%s ", label);
+       if (!ce)
+               printf("(missing)\n");
+       else if (ce == o->df_conflict_entry)
+               printf("(conflict)\n");
+       else
+               printf("%06o #%d %s %.8s\n",
+                      ce->ce_mode, ce_stage(ce), ce->name,
+                      sha1_to_hex(ce->sha1));
+}
+
+static int debug_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+{
+       int i;
+
+       printf("* %d-way merge\n", o->merge_size);
+       debug_stage("index", stages[0], o);
+       for (i = 1; i <= o->merge_size; i++) {
+               char buf[24];
+               sprintf(buf, "ent#%d", i);
+               debug_stage(buf, stages[i], o);
+       }
+       return 0;
+}
+
 static struct lock_file lock_file;
 
 int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
@@ -101,6 +129,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                            "don't check the working tree after merging", 1),
                OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
                            "skip applying sparse checkout filter", 1),
+               OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack,
+                           "debug unpack-trees", 1),
                OPT_END()
        };
 
@@ -169,6 +199,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                        opts.head_idx = 1;
        }
 
+       if (opts.debug_unpack)
+               opts.fn = debug_merge;
+
        cache_tree_free(&active_cache_tree);
        for (i = 0; i < nr_trees; i++) {
                struct tree *tree = trees[i];
@@ -178,6 +211,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        if (unpack_trees(nr_trees, t, &opts))
                return 128;
 
+       if (opts.debug_unpack)
+               return 0; /* do not write the index out */
+
        /*
         * When reading only one tree (either the most basic form,
         * "-m ent" or "--reset ent" form), we can obtain a fully
diff --git a/cache.h b/cache.h
index 7d5c21e3f1e737504a7f4f8c76781b1552cb77e3..b3370eb41e500b59bdf9d03679e7c17dde59e911 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -182,6 +182,8 @@ struct cache_entry {
 /* Only remove in work directory, not index */
 #define CE_WT_REMOVE (0x400000)
 
+#define CE_UNPACKED  (0x1000000)
+
 /*
  * Extended on-disk flags
  */
index db965c9982f1f2ae7e08331d3fa1d40e194a6520..3a125621465a68a267d08f74d842238a078c51b2 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -613,8 +613,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                        NULL
                };
                conn->env = env;
-               *arg++ = "sh";
-               *arg++ = "-c";
+               conn->use_shell = 1;
        }
        *arg++ = cmd.buf;
        *arg = NULL;
index 899034d354e4f009a1e9aab1e49f01e856a3a30a..d7e13cb177a3c345eb076a9ffede87c6e6afa367 100644 (file)
@@ -382,21 +382,6 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        show_modified(revs, tree, idx, 1, cached, match_missing);
 }
 
-static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
-{
-       int len = ce_namelen(ce);
-       const struct index_state *index = o->src_index;
-
-       while (o->pos < index->cache_nr) {
-               struct cache_entry *next = index->cache[o->pos];
-               if (len != ce_namelen(next))
-                       break;
-               if (memcmp(ce->name, next->name, len))
-                       break;
-               o->pos++;
-       }
-}
-
 /*
  * The unpack_trees() interface is designed for merging, so
  * the different source entries are designed primarily for
@@ -418,9 +403,6 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
        struct cache_entry *tree = src[1];
        struct rev_info *revs = o->unpack_data;
 
-       if (idx && ce_stage(idx))
-               skip_same_name(idx, o);
-
        /*
         * Unpack-trees generates a DF/conflict entry if
         * there was a directory in the index and a tree
@@ -466,6 +448,7 @@ int run_diff_index(struct rev_info *revs, int cached)
                exit(128);
 
        diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
+       diffcore_fix_diff_index(&revs->diffopt);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
diff --git a/diff.c b/diff.c
index 0190ec6d9f295fbd92b2cd3561b29ca0d0e0af1a..381cc8d4fd69ca31fb8fc8af31422160e3ec1fd3 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3678,6 +3678,23 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
        *q = outq;
 }
 
+static int diffnamecmp(const void *a_, const void *b_)
+{
+       const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+       const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+       const char *name_a, *name_b;
+
+       name_a = a->one ? a->one->path : a->two->path;
+       name_b = b->one ? b->one->path : b->two->path;
+       return strcmp(name_a, name_b);
+}
+
+void diffcore_fix_diff_index(struct diff_options *options)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       qsort(q->queue, q->nr, sizeof(q->queue[0]), diffnamecmp);
+}
+
 void diffcore_std(struct diff_options *options)
 {
        if (options->skip_stat_unmatch)
diff --git a/diff.h b/diff.h
index 968a8dce95b93d76e299c070ffa3587c31fcc965..2ef3341fb0852fc8958fa5c5eacab69ee68c0ad9 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -210,6 +210,7 @@ extern int diff_setup_done(struct diff_options *);
 #define DIFF_PICKAXE_REGEX     2
 
 extern void diffcore_std(struct diff_options *);
+extern void diffcore_fix_diff_index(struct diff_options *);
 
 #define COMMON_DIFF_OPTIONS_HELP \
 "\ncommon diff options:\n" \
index 2f46fda47bc1aa0a345c5cb7293724497965d75e..c8b9cbb101f1319b6efd78ff6ae9c63efcdc991b 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -205,7 +205,8 @@ check_patch_format () {
                        # discarding the indented remainder of folded lines,
                        # and see if it looks like that they all begin with the
                        # header field names...
-                       sed -n -e '/^$/q' -e '/^[       ]/d' -e p "$1" |
+                       tr -d '\015' <"$1" |
+                       sed -n -e '/^$/q' -e '/^[       ]/d' -e p |
                        sane_egrep -v '^[!-9;-~]+:' >/dev/null ||
                        patch_format=mbox
                fi
index 195b5ef48ed3371b2010f9caf711e12089da9c06..81fd3dba3de9671a1202bea389ad192cdd31c6ef 100755 (executable)
@@ -462,11 +462,11 @@ if [ "$filter_tag_name" ]; then
                                                "$new_sha1" "$new_ref"
                                git cat-file tag "$ref" |
                                sed -n \
-                                   -e "1,/^$/{
+                                   -e '1,/^$/{
                                          /^object /d
                                          /^type /d
                                          /^tag /d
-                                       }" \
+                                       }' \
                                    -e '/^-----BEGIN PGP SIGNATURE-----/q' \
                                    -e 'p' ) |
                                git mktag) ||
index b3580e9e48b6ac5d1a7fbd010f032445702f140f..197b55edf3eeeaec345d88227825cc550e6de72d 100644 (file)
@@ -7,7 +7,7 @@ all::
 # TCL_PATH must be vaild for this to work.
 #
 
-GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
 -include GIT-VERSION-FILE
 
@@ -270,7 +270,7 @@ TRACK_VARS = \
        GITGUI_MACOSXAPP=$(GITGUI_MACOSXAPP) \
 #end TRACK_VARS
 
-GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
+GIT-GUI-VARS: FORCE
        @VARS='$(TRACK_VARS)'; \
        if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
                echo 1>&2 "    * new locations or Tcl/Tk interpreter"; \
@@ -340,5 +340,4 @@ ifdef GITGUI_WINDOWS_WRAPPER
 endif
 
 .PHONY: all install uninstall dist-version clean
-.PHONY: .FORCE-GIT-VERSION-FILE
-.PHONY: .FORCE-GIT-GUI-VARS
+.PHONY: FORCE
index 718277a651f1fbc8e100e7eb657d87699b1059cb..1fb3cbfc78fe07e658aa299f46fd8f55dbab1378 100755 (executable)
@@ -1074,6 +1074,8 @@ if {[catch {
                set _prefix {}
                }]
        && [catch {
+               # beware that from the .git dir this sets _gitdir to .
+               # and _prefix to the empty string
                set _gitdir [git rev-parse --git-dir]
                set _prefix [git rev-parse --show-prefix]
        } err]} {
@@ -1082,6 +1084,14 @@ if {[catch {
        choose_repository::pick
        set picked 1
 }
+
+# we expand the _gitdir when it's just a single dot (i.e. when we're being
+# run from the .git dir itself) lest the routines to find the worktree
+# get confused
+if {$_gitdir eq "."} {
+       set _gitdir [pwd]
+}
+
 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
        catch {set _gitdir [exec cygpath --windows $_gitdir]}
 }
@@ -1613,6 +1623,9 @@ proc merge_state {path new_state {head_info {}} {index_info {}}} {
        } elseif {$s0 ne {_} && [string index $state 0] eq {_}
                && $head_info eq {}} {
                set head_info $index_info
+       } elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
+               set index_info $head_info
+               set head_info {}
        }
 
        set file_states($path) [list $s0$s1 $icon \
@@ -1941,7 +1954,7 @@ proc do_gitk {revs} {
                cd [file dirname [gitdir]]
                set env(GIT_DIR) [file tail [gitdir]]
 
-               eval exec $cmd $revs &
+               eval exec $cmd $revs "--" "--" &
 
                if {$old_GIT_DIR eq {}} {
                        unset env(GIT_DIR)
@@ -2543,12 +2556,14 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
                [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
        .mbar.commit add command -label [mc "Unstage From Commit"] \
-               -command do_unstage_selection
+               -command do_unstage_selection \
+               -accelerator $M1T-U
        lappend disable_on_lock \
                [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
        .mbar.commit add command -label [mc "Revert Changes"] \
-               -command do_revert_selection
+               -command do_revert_selection \
+               -accelerator $M1T-J
        lappend disable_on_lock \
                [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
@@ -3296,6 +3311,10 @@ unset gws
 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
 bind $ui_comm <$M1B-Key-t> {do_add_selection;break}
 bind $ui_comm <$M1B-Key-T> {do_add_selection;break}
+bind $ui_comm <$M1B-Key-u> {do_unstage_selection;break}
+bind $ui_comm <$M1B-Key-U> {do_unstage_selection;break}
+bind $ui_comm <$M1B-Key-j> {do_revert_selection;break}
+bind $ui_comm <$M1B-Key-J> {do_revert_selection;break}
 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
index 633cc572bbd076ff957c0b8194b6645e899e53ad..3f8f3030fadf4f438dd04b41ed75cfda1478f2de 100644 (file)
@@ -235,6 +235,8 @@ proc _get_recentrepos {} {
        foreach p [get_config gui.recentrepo] {
                if {[_is_git [file join $p .git]]} {
                        lappend recent $p
+               } else {
+                       _unset_recentrepo $p
                }
        }
        return [lsort $recent]
@@ -243,6 +245,7 @@ proc _get_recentrepos {} {
 proc _unset_recentrepo {p} {
        regsub -all -- {([()\[\]{}\.^$+*?\\])} $p {\\\1} p
        git config --global --unset gui.recentrepo "^$p\$"
+       load_config 1
 }
 
 proc _append_recentrepos {path} {
@@ -261,6 +264,7 @@ proc _append_recentrepos {path} {
 
        lappend recent $path
        git config --global --add gui.recentrepo $path
+       load_config 1
 
        while {[llength $recent] > 10} {
                _unset_recentrepo [lindex $recent 0]
index bd5d189ed1ec1a6520b7e9fefc4d5d6f36b9a007..066755b864b587e073a820aa2eb729a6c930d7b0 100644 (file)
@@ -664,6 +664,7 @@ proc apply_line {x y} {
                }
                set i_l $next_l
        }
+       set patch "$patch$pre_context"
        set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch"
 
        if {[catch {
index d33896a0ce26dd34c8024e21c71123e62832b8c0..0b58bd88768d38782131bfc7916f652a606e6403 100644 (file)
@@ -14,29 +14,31 @@ proc _close_updateindex {fd after} {
                toplevel $w
                wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
                wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
-               pack [label $w.msg \
-                       -justify left \
-                       -anchor w \
-                       -text [strcat \
-                               [mc "Updating the Git index failed.  A rescan will be automatically started to resynchronize git-gui."] \
-                               "\n\n$err"] \
-                       ] -anchor w
-
-               frame $w.buttons
-               button $w.buttons.continue \
+               set s [mc "Updating the Git index failed.  A rescan will be automatically started to resynchronize git-gui."]
+               text $w.msg -yscrollcommand [list $w.vs set] \
+                       -width [string length $s] -relief flat \
+                       -borderwidth 0 -highlightthickness 0 \
+                       -background [$w cget -background]
+               $w.msg tag configure bold -font font_uibold -justify center
+               scrollbar $w.vs -command [list $w.msg yview]
+               $w.msg insert end $s bold \n\n$err {}
+               $w.msg configure -state disabled
+
+               button $w.continue \
                        -text [mc "Continue"] \
                        -command [list destroy $w]
-               pack $w.buttons.continue -side right -padx 5
-               button $w.buttons.unlock \
+               button $w.unlock \
                        -text [mc "Unlock Index"] \
                        -command "destroy $w; _delete_indexlock"
-               pack $w.buttons.unlock -side right
-               pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+               grid $w.msg - $w.vs -sticky news
+               grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2
+               grid columnconfigure $w 0 -weight 1
+               grid rowconfigure $w 0 -weight 1
 
                wm protocol $w WM_DELETE_WINDOW update
-               bind $w.buttons.continue <Visibility> "
+               bind $w.continue <Visibility> "
                        grab $w
-                       focus $w.buttons.continue
+                       focus %W
                "
                tkwait window $w
 
index b8e6456208d8cccb669eb125cfb2ac677443db82..6a65f255cc63cc7a6d0ae0fc0ce4b65298a40e82 100755 (executable)
@@ -320,7 +320,7 @@ EOF
        else
                # plain-old CGI
                resolve_full_httpd
-               list_mods=$(echo "$full_httpd" | sed "s/-f$/-l/")
+               list_mods=$(echo "$full_httpd" | sed 's/-f$/-l/')
                $list_mods | sane_grep 'mod_cgi\.c' >/dev/null 2>&1 || \
                if test -f "$module_path/mod_cgi.so"
                then
index 2de4c3aa70c10203aebe4e117b30422e39f41e02..38331a861106c63bf5f421dbe03f4aafe949812e 100755 (executable)
@@ -275,7 +275,7 @@ true)
 *)
        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"
+       eval="$eval \"\$merge_name\" HEAD $merge_head $verbosity"
        ;;
 esac
 eval "exec $eval"
index 9187e9bdba4792ab9d0f5532128ff87e9329ab1e..3e4fd1456f1ebb4aabb61de6d7f13f820ae2abdc 100755 (executable)
@@ -378,7 +378,7 @@ update_squash_messages () {
                        sed -e 1d -e '2,/^./{
                                /^$/d
                        }' <"$SQUASH_MSG".bak
-               } >$SQUASH_MSG
+               } >"$SQUASH_MSG"
        else
                commit_message HEAD > "$FIXUP_MSG" || die "Cannot write $FIXUP_MSG"
                COUNT=2
@@ -387,7 +387,7 @@ update_squash_messages () {
                        echo "# The first commit's message is:"
                        echo
                        cat "$FIXUP_MSG"
-               } >$SQUASH_MSG
+               } >"$SQUASH_MSG"
        fi
        case $1 in
        squash)
@@ -403,11 +403,11 @@ update_squash_messages () {
                echo
                commit_message $2 | sed -e 's/^/#       /'
                ;;
-       esac >>$SQUASH_MSG
+       esac >>"$SQUASH_MSG"
 }
 
 peek_next_command () {
-       sed -n -e "/^#/d" -e "/^$/d" -e "s/ .*//p" -e "q" < "$TODO"
+       sed -n -e "/^#/d" -e '/^$/d' -e "s/ .*//p" -e "q" < "$TODO"
 }
 
 # A squash/fixup has failed.  Prepare the long version of the squash
index eddc02875f3802844a4c284aaedef48417f4580d..b5d9178275d7907e5ed580c1c4bfc9256adcc7ba 100755 (executable)
@@ -85,10 +85,8 @@ continue_merge () {
                        printf "Already applied: %0${prec}d " $msgnum
                fi
        fi
-       if test -z "$GIT_QUIET"
-       then
-               git rev-list --pretty=oneline -1 "$cmt" | sed -e 's/^[^ ]* //'
-       fi
+       test -z "$GIT_QUIET" &&
+       git log --format=%s -1 "$cmt"
 
        prev_head=`git rev-parse HEAD^0`
        # save the resulting commit so we can read-tree on it later
index 2a25b7cc470a25572e8741803b4e7124933feff5..9c66928c4af8cf37b9c13a6a40936523e16fcc6f 100644 (file)
@@ -779,7 +779,12 @@ function handleResponse() {
        }
 
        // the server returned error
-       if (xhr.readyState === 3 && xhr.status !== 200) {
+       // try ... catch block is to work around bug in IE8
+       try {
+               if (xhr.readyState === 3 && xhr.status !== 200) {
+                       return;
+               }
+       } catch (e) {
                return;
        }
        if (xhr.readyState === 4 && xhr.status !== 200) {
diff --git a/grep.c b/grep.c
index 8e1f7de7717da95e8954491f5205547f2091aa35..452c2cbae4c4b706c867574c437490cdf7ac9bd5 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -640,8 +640,15 @@ static int look_ahead(struct grep_opt *opt,
 
                if (p->fixed)
                        hit = !fixmatch(p->pattern, bol, p->ignore_case, &m);
-               else
+               else {
+#ifdef REG_STARTEND
+                       m.rm_so = 0;
+                       m.rm_eo = *left_p;
+                       hit = !regexec(&p->regexp, bol, 1, &m, REG_STARTEND);
+#else
                        hit = !regexec(&p->regexp, bol, 1, &m, 0);
+#endif
+               }
                if (!hit || m.rm_so < 0 || m.rm_eo < 0)
                        continue;
                if (earliest < 0 || m.rm_so < earliest)
index 4bddeb591ecc17ec532164d0d6cf1ad1a54eb996..75a35fcd0699940aea51534b5fe954847bed7bb9 100644 (file)
@@ -1,7 +1,5 @@
 :
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*  / X X \1#       /'
 compare_diff_raw () {
     # When heuristics are improved, the score numbers would change.
index 4e72b53140bd35db87a6c873eda9e75e896e1cdd..c3e7e322a8d62b988999b166d2abcf08930adbf7 100755 (executable)
@@ -4,21 +4,8 @@ test_description='CRLF conversion'
 
 . ./test-lib.sh
 
-q_to_nul () {
-       perl -pe 'y/Q/\000/'
-}
-
-q_to_cr () {
-       tr Q '\015'
-}
-
-append_cr () {
-       sed -e 's/$/Q/' | tr Q '\015'
-}
-
-remove_cr () {
-       tr '\015' Q <"$1" | grep Q >/dev/null &&
-       tr '\015' Q <"$1" | sed -ne 's/Q$//p'
+has_cr() {
+       tr '\015' Q <"$1" | grep Q >/dev/null
 }
 
 test_expect_success setup '
@@ -156,7 +143,7 @@ test_expect_success 'checkout with autocrlf=true' '
 
        for f in one dir/two
        do
-               remove_cr "$f" >tmp && mv -f tmp $f &&
+               remove_cr <"$f" >tmp && mv -f tmp $f &&
                git update-index -- $f || {
                        echo "Eh? $f"
                        false
@@ -180,7 +167,7 @@ test_expect_success 'checkout with autocrlf=input' '
 
        for f in one dir/two
        do
-               if remove_cr "$f" >/dev/null
+               if has_cr "$f"
                then
                        echo "Eh? $f"
                        false
@@ -245,7 +232,7 @@ test_expect_success 'apply patch (autocrlf=true)' '
        git read-tree --reset -u HEAD &&
 
        git apply patch.file &&
-       test "$patched" = "`remove_cr one | git hash-object --stdin`" || {
+       test "$patched" = "`remove_cr <one | git hash-object --stdin`" || {
                echo "Eh?  apply without index"
                false
        }
@@ -272,7 +259,7 @@ test_expect_success 'apply patch --index (autocrlf=true)' '
 
        git apply --index patch.file &&
        test "$patched" = `git rev-parse :one` &&
-       test "$patched" = "`remove_cr one | git hash-object --stdin`" || {
+       test "$patched" = "`remove_cr <one | git hash-object --stdin`" || {
                echo "Eh?  apply with --index"
                false
        }
@@ -285,7 +272,7 @@ test_expect_success '.gitattributes says two is binary' '
        git config core.autocrlf true &&
        git read-tree --reset -u HEAD &&
 
-       if remove_cr dir/two >/dev/null
+       if has_cr dir/two
        then
                echo "Huh?"
                false
@@ -293,7 +280,7 @@ test_expect_success '.gitattributes says two is binary' '
                : happy
        fi &&
 
-       if remove_cr one >/dev/null
+       if has_cr one
        then
                : happy
        else
@@ -301,7 +288,7 @@ test_expect_success '.gitattributes says two is binary' '
                false
        fi &&
 
-       if remove_cr three >/dev/null
+       if has_cr three
        then
                echo "Huh?"
                false
@@ -316,7 +303,7 @@ test_expect_success '.gitattributes says two is input' '
        echo "two crlf=input" >.gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       if remove_cr dir/two >/dev/null
+       if has_cr dir/two
        then
                echo "Huh?"
                false
@@ -331,7 +318,7 @@ test_expect_success '.gitattributes says two and three are text' '
        echo "t* crlf" >.gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       if remove_cr dir/two >/dev/null
+       if has_cr dir/two
        then
                : happy
        else
@@ -339,7 +326,7 @@ test_expect_success '.gitattributes says two and three are text' '
                false
        fi &&
 
-       if remove_cr three >/dev/null
+       if has_cr three
        then
                : happy
        else
@@ -357,14 +344,14 @@ test_expect_success 'in-tree .gitattributes (1)' '
        rm -rf tmp one dir .gitattributes patch.file three &&
        git read-tree --reset -u HEAD &&
 
-       if remove_cr one >/dev/null
+       if has_cr one
        then
                echo "Eh? one should not have CRLF"
                false
        else
                : happy
        fi &&
-       remove_cr three >/dev/null || {
+       has_cr three || {
                echo "Eh? three should still have CRLF"
                false
        }
@@ -376,14 +363,14 @@ test_expect_success 'in-tree .gitattributes (2)' '
        git read-tree --reset HEAD &&
        git checkout-index -f -q -u -a &&
 
-       if remove_cr one >/dev/null
+       if has_cr one
        then
                echo "Eh? one should not have CRLF"
                false
        else
                : happy
        fi &&
-       remove_cr three >/dev/null || {
+       has_cr three || {
                echo "Eh? three should still have CRLF"
                false
        }
@@ -396,14 +383,14 @@ test_expect_success 'in-tree .gitattributes (3)' '
        git checkout-index -u .gitattributes &&
        git checkout-index -u one dir/two three &&
 
-       if remove_cr one >/dev/null
+       if has_cr one
        then
                echo "Eh? one should not have CRLF"
                false
        else
                : happy
        fi &&
-       remove_cr three >/dev/null || {
+       has_cr three || {
                echo "Eh? three should still have CRLF"
                false
        }
@@ -416,14 +403,14 @@ test_expect_success 'in-tree .gitattributes (4)' '
        git checkout-index -u one dir/two three &&
        git checkout-index -u .gitattributes &&
 
-       if remove_cr one >/dev/null
+       if has_cr one
        then
                echo "Eh? one should not have CRLF"
                false
        else
                : happy
        fi &&
-       remove_cr three >/dev/null || {
+       has_cr three || {
                echo "Eh? three should still have CRLF"
                false
        }
@@ -456,7 +443,7 @@ test_expect_success 'checkout when deleting .gitattributes' '
 
        git checkout master~1 &&
        git checkout master &&
-       remove_cr .file2 >/dev/null
+       has_cr .file2
 
 '
 
index f1e1d48869a25c6ab1d0b570eb4c0e28afc5fa05..7af3fbcc7b9e5e61e41205accaa6d81e85cae6cf 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success setup '
        test_tick &&
        git commit -m Initial &&
 
-       sed -e "s/\$/\r/" "$TEST_DIRECTORY"/t0022-crlf-rename.sh >elpmas &&
+       append_cr <"$TEST_DIRECTORY"/t0022-crlf-rename.sh >elpmas &&
        git add elpmas &&
        rm -f sample &&
 
index 22ba7a5442c587f4536aad5668df43661231de56..4f171722d9e1dda7668f54b4104267eb70d91c67 100755 (executable)
@@ -126,9 +126,6 @@ cat >expected <<\EOF
 100644 X 0     Z/NN
 EOF
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-
 check_result () {
     git ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current &&
     test_cmp expected current
index c2d408b46120ef9d962272b652a68a83ba0852a5..6327d205cb89b00e77502031e3b56b4b895cc32d 100755 (executable)
@@ -26,8 +26,6 @@ read_tree_twoway () {
     git read-tree -m "$1" "$2" && git ls-files --stage
 }
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 compare_change () {
        sed -n >current \
            -e '/^--- /d; /^+++ /d; /^@@ /d;' \
index 5e40cec530df07a8b7c088d31b28ac2d39abdc1b..0241329a08af0be642131ffe4b619231d9ea3844 100755 (executable)
@@ -10,8 +10,6 @@ This is identical to t1001, but uses -u to update the work tree as well.
 '
 . ./test-lib.sh
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 compare_change () {
        sed >current \
            -e '1{/^diff --git /d;}' \
diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh
new file mode 100755 (executable)
index 0000000..9811d46
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='read-tree D/F conflict corner cases'
+
+. ./test-lib.sh
+
+maketree () {
+       (
+               rm -f .git/index .git/index.lock &&
+               git clean -d -f -f -q -x &&
+               name="$1" &&
+               shift &&
+               for it
+               do
+                       path=$(expr "$it" : '\([^:]*\)') &&
+                       mkdir -p $(dirname "$path") &&
+                       echo "$it" >"$path" &&
+                       git update-index --add "$path" || exit
+               done &&
+               git tag "$name" $(git write-tree)
+       )
+}
+
+settree () {
+       rm -f .git/index .git/index.lock &&
+       git clean -d -f -f -q -x &&
+       git read-tree "$1" &&
+       git checkout-index -f -q -u -a &&
+       git update-index --refresh
+}
+
+checkindex () {
+       git ls-files -s |
+       sed "s|^[0-7][0-7]* $_x40 \([0-3]\)     |\1 |" >current &&
+       cat >expect &&
+       test_cmp expect current
+}
+
+test_expect_success setup '
+       maketree O-000 a/b-2/c/d a/b/c/d a/x &&
+       maketree A-000 a/b-2/c/d a/b/c/d a/x &&
+       maketree A-001 a/b-2/c/d a/b/c/d a/b/c/e a/x &&
+       maketree B-000 a/b-2/c/d a/b     a/x &&
+
+       maketree O-010 t-0     t/1  t/2 t=3 &&
+       maketree A-010 t-0 t            t=3 &&
+       maketree B-010         t/1:     t=3: &&
+
+       maketree O-020 ds/dma/ioat.c ds/dma/ioat_dca.c &&
+       maketree A-020 ds/dma/ioat/Makefile ds/dma/ioat/registers.h &&
+       :
+'
+
+test_expect_success '3-way (1)' '
+       settree A-000 &&
+       git read-tree -m -u O-000 A-000 B-000 &&
+       checkindex <<-EOF
+       3 a/b
+       0 a/b-2/c/d
+       1 a/b/c/d
+       2 a/b/c/d
+       0 a/x
+       EOF
+'
+
+test_expect_success '3-way (2)' '
+       settree A-001 &&
+       git read-tree -m -u O-000 A-001 B-000 &&
+       checkindex <<-EOF
+       3 a/b
+       0 a/b-2/c/d
+       1 a/b/c/d
+       2 a/b/c/d
+       2 a/b/c/e
+       0 a/x
+       EOF
+'
+
+test_expect_success '3-way (3)' '
+       settree A-010 &&
+       git read-tree -m -u O-010 A-010 B-010 &&
+       checkindex <<-EOF
+       2 t
+       1 t-0
+       2 t-0
+       1 t/1
+       3 t/1
+       1 t/2
+       0 t=3
+       EOF
+'
+
+test_expect_success '2-way (1)' '
+       settree O-020 &&
+       git read-tree -m -u O-020 A-020 &&
+       checkindex <<-EOF
+       0 ds/dma/ioat/Makefile
+       0 ds/dma/ioat/registers.h
+       EOF
+'
+
+test_done
diff --git a/t/t1506-rev-parse-upstream.sh b/t/t1506-rev-parse-upstream.sh
deleted file mode 100755 (executable)
index 95c9b09..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/sh
-
-test_description='test <branch>@{upstream} syntax'
-
-. ./test-lib.sh
-
-
-test_expect_success 'setup' '
-
-       test_commit 1 &&
-       git checkout -b side &&
-       test_commit 2 &&
-       git checkout master &&
-       git clone . clone &&
-       test_commit 3 &&
-       (cd clone &&
-        test_commit 4 &&
-        git branch --track my-side origin/side)
-
-'
-
-full_name () {
-       (cd clone &&
-        git rev-parse --symbolic-full-name "$@")
-}
-
-commit_subject () {
-       (cd clone &&
-        git show -s --pretty=format:%s "$@")
-}
-
-test_expect_success '@{upstream} resolves to correct full name' '
-       test refs/remotes/origin/master = "$(full_name @{upstream})"
-'
-
-test_expect_success '@{u} resolves to correct full name' '
-       test refs/remotes/origin/master = "$(full_name @{u})"
-'
-
-test_expect_success 'my-side@{upstream} resolves to correct full name' '
-       test refs/remotes/origin/side = "$(full_name my-side@{u})"
-'
-
-test_expect_success 'my-side@{u} resolves to correct commit' '
-       git checkout side &&
-       test_commit 5 &&
-       (cd clone && git fetch) &&
-       test 2 = "$(commit_subject my-side)" &&
-       test 5 = "$(commit_subject my-side@{u})"
-'
-
-test_expect_success 'not-tracking@{u} fails' '
-       test_must_fail full_name non-tracking@{u} &&
-       (cd clone && git checkout --no-track -b non-tracking) &&
-       test_must_fail full_name non-tracking@{u}
-'
-
-test_expect_success '<branch>@{u}@{1} resolves correctly' '
-       test_commit 6 &&
-       (cd clone && git fetch) &&
-       test 5 = $(commit_subject my-side@{u}@{1})
-'
-
-test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
-       git checkout HEAD^0 &&
-       test_must_fail git rev-parse @{u}
-'
-
-test_expect_success 'checkout -b new my-side@{u} forks from the same' '
-(
-       cd clone &&
-       git checkout -b new my-side@{u} &&
-       git rev-parse --symbolic-full-name my-side@{u} >expect &&
-       git rev-parse --symbolic-full-name new@{u} >actual &&
-       test_cmp expect actual
-)
-'
-
-test_expect_success 'merge my-side@{u} records the correct name' '
-(
-       sq="'\''" &&
-       cd clone || exit
-       git checkout master || exit
-       git branch -D new ;# can fail but is ok
-       git branch -t new my-side@{u} &&
-       git merge -s ours new@{u} &&
-       git show -s --pretty=format:%s >actual &&
-       echo "Merge remote branch ${sq}origin/side${sq}" >expect &&
-       test_cmp expect actual
-)
-'
-
-test_expect_success 'branch -d other@{u}' '
-       git checkout -t -b other master &&
-       git branch -d @{u} &&
-       git for-each-ref refs/heads/master >actual &&
-       >expect &&
-       test_cmp expect actual
-'
-
-test_expect_success 'checkout other@{u}' '
-       git branch -f master HEAD &&
-       git checkout -t -b another master &&
-       git checkout @{u} &&
-       git symbolic-ref HEAD >actual &&
-       echo refs/heads/master >expect &&
-       test_cmp expect actual
-'
-
-test_done
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
new file mode 100755 (executable)
index 0000000..95c9b09
--- /dev/null
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+test_description='test <branch>@{upstream} syntax'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+
+       test_commit 1 &&
+       git checkout -b side &&
+       test_commit 2 &&
+       git checkout master &&
+       git clone . clone &&
+       test_commit 3 &&
+       (cd clone &&
+        test_commit 4 &&
+        git branch --track my-side origin/side)
+
+'
+
+full_name () {
+       (cd clone &&
+        git rev-parse --symbolic-full-name "$@")
+}
+
+commit_subject () {
+       (cd clone &&
+        git show -s --pretty=format:%s "$@")
+}
+
+test_expect_success '@{upstream} resolves to correct full name' '
+       test refs/remotes/origin/master = "$(full_name @{upstream})"
+'
+
+test_expect_success '@{u} resolves to correct full name' '
+       test refs/remotes/origin/master = "$(full_name @{u})"
+'
+
+test_expect_success 'my-side@{upstream} resolves to correct full name' '
+       test refs/remotes/origin/side = "$(full_name my-side@{u})"
+'
+
+test_expect_success 'my-side@{u} resolves to correct commit' '
+       git checkout side &&
+       test_commit 5 &&
+       (cd clone && git fetch) &&
+       test 2 = "$(commit_subject my-side)" &&
+       test 5 = "$(commit_subject my-side@{u})"
+'
+
+test_expect_success 'not-tracking@{u} fails' '
+       test_must_fail full_name non-tracking@{u} &&
+       (cd clone && git checkout --no-track -b non-tracking) &&
+       test_must_fail full_name non-tracking@{u}
+'
+
+test_expect_success '<branch>@{u}@{1} resolves correctly' '
+       test_commit 6 &&
+       (cd clone && git fetch) &&
+       test 5 = $(commit_subject my-side@{u}@{1})
+'
+
+test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
+       git checkout HEAD^0 &&
+       test_must_fail git rev-parse @{u}
+'
+
+test_expect_success 'checkout -b new my-side@{u} forks from the same' '
+(
+       cd clone &&
+       git checkout -b new my-side@{u} &&
+       git rev-parse --symbolic-full-name my-side@{u} >expect &&
+       git rev-parse --symbolic-full-name new@{u} >actual &&
+       test_cmp expect actual
+)
+'
+
+test_expect_success 'merge my-side@{u} records the correct name' '
+(
+       sq="'\''" &&
+       cd clone || exit
+       git checkout master || exit
+       git branch -D new ;# can fail but is ok
+       git branch -t new my-side@{u} &&
+       git merge -s ours new@{u} &&
+       git show -s --pretty=format:%s >actual &&
+       echo "Merge remote branch ${sq}origin/side${sq}" >expect &&
+       test_cmp expect actual
+)
+'
+
+test_expect_success 'branch -d other@{u}' '
+       git checkout -t -b other master &&
+       git branch -d @{u} &&
+       git for-each-ref refs/heads/master >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'checkout other@{u}' '
+       git branch -f master HEAD &&
+       git checkout -t -b another master &&
+       git checkout @{u} &&
+       git symbolic-ref HEAD >actual &&
+       echo refs/heads/master >expect &&
+       test_cmp expect actual
+'
+
+test_done
index ee60d03fe8a582100e9df5511f6fea8900bcc543..eee0d344d24623ad9928366729c8efaea699fcd7 100755 (executable)
@@ -43,8 +43,6 @@ test_expect_success \
      tree=`git write-tree` &&
      echo $tree'
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 test_output () {
     sed -e "s/ $_x40   / X     /" <current >check
     test_cmp expected check
index 8be9fb411276e3c416374a813d691ff0455a8746..06654c6f82fb6cfe955b91bcbd4a5fa064c73e70 100755 (executable)
@@ -39,8 +39,6 @@ test_expect_success \
      tree=`git write-tree` &&
      echo $tree'
 
-_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
 test_output () {
     sed -e "s/ $_x40   / X     /" <current >check
     test_cmp expected check
index 4e6a44b623c456dc85f9daa6c4b5b1f0789c93c5..4314ad2d66d06b411e4bc0c9ee7b07553fc35ac2 100755 (executable)
@@ -134,10 +134,6 @@ test_expect_success 'rebase -q is quiet' '
      test ! -s output.out
 '
 
-q_to_cr () {
-       tr Q '\015'
-}
-
 test_expect_success 'Rebase a commit that sprinkles CRs in' '
        (
                echo "One"
index e12cd578e8663ad8717aecde310bb0b6a500c2a2..2062b858bbcb63a715d87f3b12adfd0e9ceb3a67 100755 (executable)
@@ -32,8 +32,8 @@ test_expect_success rebase '
 
        git checkout side &&
        git rebase master &&
-       git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
-       git cat-file commit side@{1} | sed -e "1,/^$/d" >expect &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+       git cat-file commit side@{1} | sed -e "1,/^\$/d" >expect &&
        test_cmp expect actual
 
 '
index 8c1b81e248bced2ccb5e4ff0067996462e89deb8..ff8c2f7532aa6fd748dbbdc0322384576f3a35ed 100755 (executable)
@@ -20,8 +20,6 @@ test_expect_success \
     'test_chmod +x rezrov &&
      git diff-index $tree >current'
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 sed -e 's/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' <current >check
 echo ":100644 100755 X X M     rezrov" >expected
 
index f64aa48d24f2f5704d07b9285c94ba983f7d92ac..bc46563afc607e6fba9acbd2773d88207c878981 100755 (executable)
@@ -77,10 +77,6 @@ test_expect_success 'apply binary patch' \
         tree1=`git write-tree` &&
         test "$tree1" = "$tree0"'
 
-q_to_nul() {
-       perl -pe 'y/Q/\000/'
-}
-
 nul_to_q() {
        perl -pe 'y/\000/Q/'
 }
index 3bc1cccf8869aef26e175e207dc2923d3ddb1e65..f2a2aaa2b9c7fd84634bb74febb3f0f5ac1793e1 100755 (executable)
@@ -93,9 +93,9 @@ test_expect_success 'extra headers' '
        git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
 " &&
        git format-patch --stdout master..side > patch2 &&
-       sed -e "/^$/q" patch2 > hdrs2 &&
-       grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs2 &&
-       grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs2
+       sed -e "/^\$/q" patch2 > hdrs2 &&
+       grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 &&
+       grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2
 
 '
 
@@ -104,9 +104,9 @@ test_expect_success 'extra headers without newlines' '
        git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
        git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
        git format-patch --stdout master..side >patch3 &&
-       sed -e "/^$/q" patch3 > hdrs3 &&
-       grep "^To: R. E. Cipient <rcipient@example.com>$" hdrs3 &&
-       grep "^Cc: S. E. Cipient <scipient@example.com>$" hdrs3
+       sed -e "/^\$/q" patch3 > hdrs3 &&
+       grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 &&
+       grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3
 
 '
 
@@ -115,32 +115,32 @@ test_expect_success 'extra headers with multiple To:s' '
        git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
        git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
        git format-patch --stdout master..side > patch4 &&
-       sed -e "/^$/q" patch4 > hdrs4 &&
-       grep "^To: R. E. Cipient <rcipient@example.com>,$" hdrs4 &&
-       grep "^ *S. E. Cipient <scipient@example.com>$" hdrs4
+       sed -e "/^\$/q" patch4 > hdrs4 &&
+       grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 &&
+       grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4
 '
 
 test_expect_success 'additional command line cc' '
 
        git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
-       git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch5 &&
-       grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch5 &&
-       grep "^ *S. E. Cipient <scipient@example.com>$" patch5
+       git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
+       grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 &&
+       grep "^ *S. E. Cipient <scipient@example.com>\$" patch5
 '
 
 test_expect_success 'command line headers' '
 
        git config --unset-all format.headers &&
-       git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch6 &&
-       grep "^Cc: R. E. Cipient <rcipient@example.com>$" patch6
+       git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
+       grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6
 '
 
 test_expect_success 'configuration headers and command line headers' '
 
        git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
-       git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^$/q" >patch7 &&
-       grep "^Cc: R. E. Cipient <rcipient@example.com>,$" patch7 &&
-       grep "^ *S. E. Cipient <scipient@example.com>$" patch7
+       git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
+       grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 &&
+       grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
 '
 
 test_expect_success 'multiple files' '
@@ -406,9 +406,9 @@ test_expect_success 'cover-letter inherits diff options' '
        git mv file foo &&
        git commit -m foo &&
        git format-patch --cover-letter -1 &&
-       ! grep "file => foo .* 0 *$" 0000-cover-letter.patch &&
+       ! grep "file => foo .* 0 *\$" 0000-cover-letter.patch &&
        git format-patch --cover-letter -1 -M &&
-       grep "file => foo .* 0 *$" 0000-cover-letter.patch
+       grep "file => foo .* 0 *\$" 0000-cover-letter.patch
 
 '
 
@@ -425,7 +425,7 @@ EOF
 test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
 
        git format-patch --cover-letter -2 &&
-       sed -e "1,/A U Thor/d" -e "/^$/q" < 0000-cover-letter.patch > output &&
+       sed -e "1,/A U Thor/d" -e "/^\$/q" < 0000-cover-letter.patch > output &&
        test_cmp expect output
 
 '
@@ -450,7 +450,7 @@ EOF
 test_expect_success 'format-patch respects -U' '
 
        git format-patch -U4 -2 &&
-       sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+       sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
        test_cmp expect output
 
 '
@@ -471,7 +471,7 @@ EOF
 test_expect_success 'format-patch -p suppresses stat' '
 
        git format-patch -p -2 &&
-       sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+       sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
        test_cmp expect output
 
 '
index 3b471b641ba2d3274dd1ab89948485ff8ce4dfdb..9671de799949f8f67f906cd3db907e3a53cf4eef 100755 (executable)
@@ -37,11 +37,11 @@ test_expect_success setup '
        # patch-2 is the same as patch-1 but is based
        # on a version that already has whitespace fixed,
        # and does not introduce whitespace breakages.
-       sed -e "s/ $//" patch-1 >patch-2 &&
+       sed -e "s/ \$//" patch-1 >patch-2 &&
 
        # If all whitespace breakages are fixed the contents
        # should look like file-fixed
-       sed -e "s/ $//" file-1 >file-fixed
+       sed -e "s/ \$//" file-1 >file-fixed
 
 '
 
index 8296605234ddc82697a51de0a09b703635b4c9f4..810b04b817c79d2b4c478f767843b4e7a42e0bed 100755 (executable)
@@ -83,6 +83,12 @@ test_expect_success setup '
                echo "X-Fake-Field: Line Three" &&
                git format-patch --stdout first | sed -e "1d"
        } > patch1.eml &&
+       {
+               echo "X-Fake-Field: Line One" &&
+               echo "X-Fake-Field: Line Two" &&
+               echo "X-Fake-Field: Line Three" &&
+               git format-patch --stdout first | sed -e "1d"
+       } | append_cr >patch1-crlf.eml &&
        sed -n -e "3,\$p" msg >file &&
        git add file &&
        test_tick &&
@@ -123,6 +129,15 @@ test_expect_success 'am applies patch e-mail not in a mbox' '
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
 '
 
+test_expect_success 'am applies patch e-mail not in a mbox with CRLF' '
+       git checkout first &&
+       git am patch1-crlf.eml &&
+       ! test -d .git/rebase-apply &&
+       test -z "$(git diff second)" &&
+       test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+       test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
 GIT_AUTHOR_NAME="Another Thor"
 GIT_AUTHOR_EMAIL="a.thor@example.com"
 GIT_COMMITTER_NAME="Co M Miter"
@@ -287,7 +302,7 @@ test_expect_success 'am --committer-date-is-author-date' '
        git checkout first &&
        test_tick &&
        git am --committer-date-is-author-date patch1 &&
-       git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+       git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
        at=$(sed -ne "/^author /s/.*> //p" head1) &&
        ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
        test "$at" = "$ct"
@@ -297,7 +312,7 @@ test_expect_success 'am without --committer-date-is-author-date' '
        git checkout first &&
        test_tick &&
        git am patch1 &&
-       git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+       git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
        at=$(sed -ne "/^author /s/.*> //p" head1) &&
        ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
        test "$at" != "$ct"
@@ -311,7 +326,7 @@ test_expect_success 'am --ignore-date' '
        git checkout first &&
        test_tick &&
        git am --ignore-date patch1 &&
-       git cat-file commit HEAD | sed -e "/^$/q" >head1 &&
+       git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
        at=$(sed -ne "/^author /s/.*> //p" head1) &&
        echo "$at" | grep "+0000"
 '
index 779a5adf554d77464f2a326a2eb919e224993746..1dc224f6fbf074434728d326b8a36160e9032192 100755 (executable)
@@ -255,7 +255,7 @@ EOF
 
 test_expect_success 'log --graph with merge' '
        git log --graph --date-order --pretty=tformat:%s |
-               sed "s/ *$//" >actual &&
+               sed "s/ *\$//" >actual &&
        test_cmp expect actual
 '
 
@@ -315,7 +315,7 @@ EOF
 test_expect_success 'log --graph with full output' '
        git log --graph --date-order --pretty=short |
                git name-rev --name-only --stdin |
-               sed "s/Merge:.*/Merge: A B/;s/ *$//" >actual &&
+               sed "s/Merge:.*/Merge: A B/;s/ *\$//" >actual &&
        test_cmp expect actual
 '
 
@@ -383,7 +383,7 @@ EOF
 
 test_expect_success 'log --graph with merge' '
        git log --graph --date-order --pretty=tformat:%s |
-               sed "s/ *$//" >actual &&
+               sed "s/ *\$//" >actual &&
        test_cmp expect actual
 '
 
diff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh
new file mode 100755 (executable)
index 0000000..8cccecc
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='git pull message generation'
+
+. ./test-lib.sh
+
+dollar='$Dollar'
+
+test_expect_success setup '
+       test_commit initial afile original &&
+       git clone . cloned &&
+       (
+               cd cloned &&
+               echo added >bfile &&
+               git add bfile &&
+               test_tick &&
+               git commit -m "add bfile"
+       ) &&
+       test_tick && test_tick &&
+       echo "original $dollar" >afile &&
+       git add afile &&
+       git commit -m "do not clobber $dollar signs"
+'
+
+test_expect_success pull '
+(
+       cd cloned &&
+       git pull --log &&
+       git log -2 &&
+       git cat-file commit HEAD >result &&
+       grep Dollar result
+)
+'
+
+test_done
index 83a8e14c6c385d6d6fbdd179d30018128e8a200f..53f54a2789557bcfd4e9843b50fe28189fa6f356 100755 (executable)
@@ -105,10 +105,8 @@ test_expect_success 'non-fast-forward push show ref status' '
 '
 
 test_expect_success 'non-fast-forward push shows help message' '
-       grep \
-"To prevent you from losing history, non-fast-forward updates were rejected
-Merge the remote changes before pushing again.  See the '"'non-fast-forward'"'
-section of '"'git push --help'"' for details." output
+       grep "To prevent you from losing history, non-fast-forward updates were rejected" \
+               output
 '
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
@@ -126,10 +124,8 @@ test_expect_success 'push fails for non-fast-forward refs unmatched by remote he
        grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
        grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output &&
 
-       grep \
-"To prevent you from losing history, non-fast-forward updates were rejected
-Merge the remote changes before pushing again.  See the '"'non-fast-forward'"'
-section of '"'git push --help'"' for details." output
+       grep "To prevent you from losing history, non-fast-forward updates were rejected" \
+               output
 '
 
 stop_httpd
index 510bb9679f29c10645c902caea020651a797aa2f..af34a1e81711ca183680c9882353bd786b55e79f 100755 (executable)
@@ -8,9 +8,6 @@ note () {
        git tag "$1"
 }
 
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
-
 unnote () {
        git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\)) |\1 |g"
 }
index 7dcf39191476f272431e19e10ebb299d6aa55bb1..6291307cd03e4e374e640dc82ddc8f26dbb8ff1d 100755 (executable)
@@ -146,8 +146,8 @@ test_expect_success 'binary files cannot be merged' '
        grep "Cannot merge binary files" merge.err
 '
 
-sed -e "s/deerit.$/deerit;/" -e "s/me;$/me./" < new5.txt > new6.txt
-sed -e "s/deerit.$/deerit,/" -e "s/me;$/me,/" < new5.txt > new7.txt
+sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" < new5.txt > new6.txt
+sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" < new5.txt > new7.txt
 
 test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
 
index 75d9602de4d4238b4182956127540525f711d33f..e8d65eefb521cc9f2e8211918734d069381772d4 100755 (executable)
@@ -1,13 +1,5 @@
 #!/bin/sh
 
-append_cr () {
-       sed -e 's/$/Q/' | tr Q '\015'
-}
-
-remove_cr () {
-       tr '\015' Q | sed -e 's/Q$//'
-}
-
 test_description='merge conflict in crlf repo
 
                b---M
index 5b96fb0b37b8397342796fd3b9fec94c91063ba4..d1b22871c40674582e6999804102afb95283b653 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success 'setup for merge test' '
        git tag baseline
 '
 
-test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_failure 'do not lose a/b-2/c/d in merge (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve master &&
@@ -74,7 +74,7 @@ test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
        git tag test2
 '
 
-test_expect_failure 'merge should not have conflicts (resolve)' '
+test_expect_success 'merge should not have conflicts (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve test2 &&
index 8eec0fa9bc7278981b9a16571c588ede0a94d341..9f5c3edb0392c321092e56e2bf46fd096f74cf75 100755 (executable)
@@ -150,7 +150,7 @@ EOF
 test_expect_success '--signoff' '
        echo "yet another content *narf*" >> foo &&
        echo "zort" | git commit -s -F - foo &&
-       git cat-file commit HEAD | sed "1,/^$/d" > output &&
+       git cat-file commit HEAD | sed "1,/^\$/d" > output &&
        test_cmp expect output
 '
 
index 752adaac850862caf50e3573338b9cb742781147..c09f37528811f9395d63e3f3acd1f598307983a2 100755 (executable)
@@ -186,8 +186,8 @@ test_expect_success 'Prompting works' '
                --smtp-server="$(pwd)/fake.sendmail" \
                $patches \
                2>errors &&
-               grep "^From: Example <from@example.com>$" msgtxt1 &&
-               grep "^To: to@example.com$" msgtxt1
+               grep "^From: Example <from@example.com>\$" msgtxt1 &&
+               grep "^To: to@example.com\$" msgtxt1
 '
 
 test_expect_success 'cccmd works' '
@@ -236,7 +236,7 @@ test_expect_success 'Author From: in message body' '
                --to=nobody@example.com \
                --smtp-server="$(pwd)/fake.sendmail" \
                $patches &&
-       sed "1,/^$/d" < msgtxt1 > msgbody1
+       sed "1,/^\$/d" < msgtxt1 > msgbody1
        grep "From: A <author@example.com>" msgbody1
 '
 
@@ -247,7 +247,7 @@ test_expect_success 'Author From: not in message body' '
                --to=nobody@example.com \
                --smtp-server="$(pwd)/fake.sendmail" \
                $patches &&
-       sed "1,/^$/d" < msgtxt1 > msgbody1
+       sed "1,/^\$/d" < msgtxt1 > msgbody1
        ! grep "From: A <author@example.com>" msgbody1
 '
 
index c2ec3cb4bd97693ef8f8f25064297f019053e03a..4327eb8baa2b00b0833fdbceac27bb8291370bda 100755 (executable)
@@ -96,7 +96,7 @@ EOF
 
 test_expect_success 'pserver authentication' \
   'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'pserver authentication failure (non-anonymous user)' \
   'if cat request-git | git-cvsserver pserver >log 2>&1
@@ -105,11 +105,11 @@ test_expect_success 'pserver authentication failure (non-anonymous user)' \
    else
        true
    fi &&
-   sed -ne \$p log | grep "^I HATE YOU$"'
+   sed -ne \$p log | grep "^I HATE YOU\$"'
 
 test_expect_success 'pserver authentication (login)' \
   'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
   'if cat login-git | git-cvsserver pserver >log 2>&1
@@ -118,7 +118,7 @@ test_expect_success 'pserver authentication failure (login/non-anonymous user)'
    else
        true
    fi &&
-   sed -ne \$p log | grep "^I HATE YOU$"'
+   sed -ne \$p log | grep "^I HATE YOU\$"'
 
 
 # misuse pserver authentication for testing of req_Root
@@ -156,7 +156,7 @@ test_expect_success 'req_Root failure (conflicting roots)' \
 
 test_expect_success 'req_Root (strict paths)' \
   'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (strict-paths)' '
     ! cat request-anonymous |
@@ -165,7 +165,7 @@ test_expect_success 'req_Root failure (strict-paths)' '
 
 test_expect_success 'req_Root (w/o strict-paths)' \
   'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
     ! cat request-anonymous |
@@ -183,7 +183,7 @@ EOF
 
 test_expect_success 'req_Root (base-path)' \
   'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (base-path)' '
     ! cat request-anonymous |
@@ -194,14 +194,14 @@ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
 test_expect_success 'req_Root (export-all)' \
   'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 test_expect_success 'req_Root failure (export-all w/o whitelist)' \
   '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
 
 test_expect_success 'req_Root (everything together)' \
   'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
-   sed -ne \$p log | grep "^I LOVE YOU$"'
+   sed -ne \$p log | grep "^I LOVE YOU\$"'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
 
index 40637d6782c3ad5ffbff99d352206b8b2ef01b43..ed7b513f3e82706f6455bb32f4f19a5161bb0415 100755 (executable)
@@ -11,14 +11,6 @@ repository using cvs CLI client via git-cvsserver server'
 
 . ./test-lib.sh
 
-q_to_nul () {
-    perl -pe 'y/Q/\000/'
-}
-
-q_to_cr () {
-    tr Q '\015'
-}
-
 marked_as () {
     foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
     if [ x"$foundEntry" = x"" ] ; then
index c1476f9a2391396a201a919025963b82f7e14217..afd3053f96b789a73274217b384d245583500c04 100644 (file)
@@ -74,6 +74,12 @@ case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
                ;;
 esac
 
+# Convenience
+#
+# A regexp to match 5 and 40 hexdigits
+_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+
 # Each test should start with something like this, after copyright notices:
 #
 # test_description='Description of this test...
@@ -224,6 +230,22 @@ test_decode_color () {
                -e 's/.\[m/<RESET>/g'
 }
 
+q_to_nul () {
+       perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+       tr Q '\015'
+}
+
+append_cr () {
+       sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+       tr '\015' Q | sed -e 's/Q$//'
+}
+
 test_tick () {
        if test -z "${test_tick+set}"
        then
index 02e2aed7737207225f1b96eed774a1b75dd6d8d9..08796c23228fbfb6eb255c479f0196768ee83b27 100644 (file)
@@ -60,13 +60,6 @@ void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
        return buf;
 }
 
-static int entry_compare(struct name_entry *a, struct name_entry *b)
-{
-       return df_name_compare(
-                       a->path, tree_entry_len(a->path, a->sha1), a->mode,
-                       b->path, tree_entry_len(b->path, b->sha1), b->mode);
-}
-
 static void entry_clear(struct name_entry *a)
 {
        memset(a, 0, sizeof(*a));
@@ -138,66 +131,264 @@ char *make_traverse_path(char *path, const struct traverse_info *info, const str
        return path;
 }
 
+struct tree_desc_skip {
+       struct tree_desc_skip *prev;
+       const void *ptr;
+};
+
+struct tree_desc_x {
+       struct tree_desc d;
+       struct tree_desc_skip *skip;
+};
+
+static int name_compare(const char *a, int a_len,
+                       const char *b, int b_len)
+{
+       int len = (a_len < b_len) ? a_len : b_len;
+       int cmp = memcmp(a, b, len);
+       if (cmp)
+               return cmp;
+       return (a_len - b_len);
+}
+
+static int check_entry_match(const char *a, int a_len, const char *b, int b_len)
+{
+       /*
+        * The caller wants to pick *a* from a tree or nothing.
+        * We are looking at *b* in a tree.
+        *
+        * (0) If a and b are the same name, we are trivially happy.
+        *
+        * There are three possibilities where *a* could be hiding
+        * behind *b*.
+        *
+        * (1) *a* == "t",   *b* == "ab"  i.e. *b* sorts earlier than *a* no
+        *                                matter what.
+        * (2) *a* == "t",   *b* == "t-2" and "t" is a subtree in the tree;
+        * (3) *a* == "t-2", *b* == "t"   and "t-2" is a blob in the tree.
+        *
+        * Otherwise we know *a* won't appear in the tree without
+        * scanning further.
+        */
+
+       int cmp = name_compare(a, a_len, b, b_len);
+
+       /* Most common case first -- reading sync'd trees */
+       if (!cmp)
+               return cmp;
+
+       if (0 < cmp) {
+               /* a comes after b; it does not matter if it is case (3)
+               if (b_len < a_len && !memcmp(a, b, b_len) && a[b_len] < '/')
+                       return 1;
+               */
+               return 1; /* keep looking */
+       }
+
+       /* b comes after a; are we looking at case (2)? */
+       if (a_len < b_len && !memcmp(a, b, a_len) && b[a_len] < '/')
+               return 1; /* keep looking */
+
+       return -1; /* a cannot appear in the tree */
+}
+
+/*
+ * From the extended tree_desc, extract the first name entry, while
+ * paying attention to the candidate "first" name.  Most importantly,
+ * when looking for an entry, if there are entries that sorts earlier
+ * in the tree object representation than that name, skip them and
+ * process the named entry first.  We will remember that we haven't
+ * processed the first entry yet, and in the later call skip the
+ * entry we processed early when update_extended_entry() is called.
+ *
+ * E.g. if the underlying tree object has these entries:
+ *
+ *    blob    "t-1"
+ *    blob    "t-2"
+ *    tree    "t"
+ *    blob    "t=1"
+ *
+ * and the "first" asks for "t", remember that we still need to
+ * process "t-1" and "t-2" but extract "t".  After processing the
+ * entry "t" from this call, the caller will let us know by calling
+ * update_extended_entry() that we can remember "t" has been processed
+ * already.
+ */
+
+static void extended_entry_extract(struct tree_desc_x *t,
+                                  struct name_entry *a,
+                                  const char *first,
+                                  int first_len)
+{
+       const char *path;
+       int len;
+       struct tree_desc probe;
+       struct tree_desc_skip *skip;
+
+       /*
+        * Extract the first entry from the tree_desc, but skip the
+        * ones that we already returned in earlier rounds.
+        */
+       while (1) {
+               if (!t->d.size) {
+                       entry_clear(a);
+                       break; /* not found */
+               }
+               entry_extract(&t->d, a);
+               for (skip = t->skip; skip; skip = skip->prev)
+                       if (a->path == skip->ptr)
+                               break; /* found */
+               if (!skip)
+                       break;
+               /* We have processed this entry already. */
+               update_tree_entry(&t->d);
+       }
+
+       if (!first || !a->path)
+               return;
+
+       /*
+        * The caller wants "first" from this tree, or nothing.
+        */
+       path = a->path;
+       len = tree_entry_len(a->path, a->sha1);
+       switch (check_entry_match(first, first_len, path, len)) {
+       case -1:
+               entry_clear(a);
+       case 0:
+               return;
+       default:
+               break;
+       }
+
+       /*
+        * We need to look-ahead -- we suspect that a subtree whose
+        * name is "first" may be hiding behind the current entry "path".
+        */
+       probe = t->d;
+       while (probe.size) {
+               entry_extract(&probe, a);
+               path = a->path;
+               len = tree_entry_len(a->path, a->sha1);
+               switch (check_entry_match(first, first_len, path, len)) {
+               case -1:
+                       entry_clear(a);
+               case 0:
+                       return;
+               default:
+                       update_tree_entry(&probe);
+                       break;
+               }
+               /* keep looking */
+       }
+       entry_clear(a);
+}
+
+static void update_extended_entry(struct tree_desc_x *t, struct name_entry *a)
+{
+       if (t->d.entry.path == a->path) {
+               update_tree_entry(&t->d);
+       } else {
+               /* we have returned this entry early */
+               struct tree_desc_skip *skip = xmalloc(sizeof(*skip));
+               skip->ptr = a->path;
+               skip->prev = t->skip;
+               t->skip = skip;
+       }
+}
+
+static void free_extended_entry(struct tree_desc_x *t)
+{
+       struct tree_desc_skip *p, *s;
+
+       for (s = t->skip; s; s = p) {
+               p = s->prev;
+               free(s);
+       }
+}
+
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 {
        int ret = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
+       int i;
+       struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
+
+       for (i = 0; i < n; i++)
+               tx[i].d = t[i];
 
        for (;;) {
-               unsigned long mask = 0;
-               unsigned long dirmask = 0;
-               int i, last;
+               unsigned long mask, dirmask;
+               const char *first = NULL;
+               int first_len = 0;
+               struct name_entry *e;
+               int len;
 
-               last = -1;
                for (i = 0; i < n; i++) {
-                       if (!t[i].size)
+                       e = entry + i;
+                       extended_entry_extract(tx + i, e, NULL, 0);
+               }
+
+               /*
+                * A tree may have "t-2" at the current location even
+                * though it may have "t" that is a subtree behind it,
+                * and another tree may return "t".  We want to grab
+                * all "t" from all trees to match in such a case.
+                */
+               for (i = 0; i < n; i++) {
+                       e = entry + i;
+                       if (!e->path)
                                continue;
-                       entry_extract(t+i, entry+i);
-                       if (last >= 0) {
-                               int cmp = entry_compare(entry+i, entry+last);
-
-                               /*
-                                * Is the new name bigger than the old one?
-                                * Ignore it
-                                */
-                               if (cmp > 0)
+                       len = tree_entry_len(e->path, e->sha1);
+                       if (!first) {
+                               first = e->path;
+                               first_len = len;
+                               continue;
+                       }
+                       if (name_compare(e->path, len, first, first_len) < 0) {
+                               first = e->path;
+                               first_len = len;
+                       }
+               }
+
+               if (first) {
+                       for (i = 0; i < n; i++) {
+                               e = entry + i;
+                               extended_entry_extract(tx + i, e, first, first_len);
+                               /* Cull the ones that are not the earliest */
+                               if (!e->path)
                                        continue;
-                               /*
-                                * Is the new name smaller than the old one?
-                                * Ignore all old ones
-                                */
-                               if (cmp < 0)
-                                       mask = 0;
+                               len = tree_entry_len(e->path, e->sha1);
+                               if (name_compare(e->path, len, first, first_len))
+                                       entry_clear(e);
                        }
+               }
+
+               /* Now we have in entry[i] the earliest name from the trees */
+               mask = 0;
+               dirmask = 0;
+               for (i = 0; i < n; i++) {
+                       if (!entry[i].path)
+                               continue;
                        mask |= 1ul << i;
                        if (S_ISDIR(entry[i].mode))
                                dirmask |= 1ul << i;
-                       last = i;
                }
                if (!mask)
                        break;
-               dirmask &= mask;
-
-               /*
-                * Clear all the unused name-entries.
-                */
-               for (i = 0; i < n; i++) {
-                       if (mask & (1ul << i))
-                               continue;
-                       entry_clear(entry + i);
-               }
                ret = info->fn(n, mask, dirmask, entry, info);
                if (ret < 0)
                        break;
-               if (ret)
-                       mask &= ret;
+               mask &= ret;
                ret = 0;
-               for (i = 0; i < n; i++) {
+               for (i = 0; i < n; i++)
                        if (mask & (1ul << i))
-                               update_tree_entry(t + i);
-               }
+                               update_extended_entry(tx + i, entry + i);
        }
        free(entry);
+       for (i = 0; i < n; i++)
+               free_extended_entry(tx + i);
+       free(tx);
        return ret;
 }
 
index 0ddbef3e6355da40a1293a6a7c49b4bc5d75b842..75f54cac97f62ddaad736c2cd582cc6cdeaaebfa 100644 (file)
@@ -198,23 +198,142 @@ static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_o
        return ret;
 }
 
-static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+static void mark_ce_used(struct cache_entry *ce, struct unpack_trees_options *o)
+{
+       ce->ce_flags |= CE_UNPACKED;
+
+       if (o->cache_bottom < o->src_index->cache_nr &&
+           o->src_index->cache[o->cache_bottom] == ce) {
+               int bottom = o->cache_bottom;
+               while (bottom < o->src_index->cache_nr &&
+                      o->src_index->cache[bottom]->ce_flags & CE_UNPACKED)
+                       bottom++;
+               o->cache_bottom = bottom;
+       }
+}
+
+static void mark_all_ce_unused(struct index_state *index)
+{
+       int i;
+       for (i = 0; i < index->cache_nr; i++)
+               index->cache[i]->ce_flags &= ~CE_UNPACKED;
+}
+
+static int locate_in_src_index(struct cache_entry *ce,
+                              struct unpack_trees_options *o)
+{
+       struct index_state *index = o->src_index;
+       int len = ce_namelen(ce);
+       int pos = index_name_pos(index, ce->name, len);
+       if (pos < 0)
+               pos = -1 - pos;
+       return pos;
+}
+
+/*
+ * We call unpack_index_entry() with an unmerged cache entry
+ * only in diff-index, and it wants a single callback.  Skip
+ * the other unmerged entry with the same name.
+ */
+static void mark_ce_used_same_name(struct cache_entry *ce,
+                                  struct unpack_trees_options *o)
+{
+       struct index_state *index = o->src_index;
+       int len = ce_namelen(ce);
+       int pos;
+
+       for (pos = locate_in_src_index(ce, o); pos < index->cache_nr; pos++) {
+               struct cache_entry *next = index->cache[pos];
+               if (len != ce_namelen(next) ||
+                   memcmp(ce->name, next->name, len))
+                       break;
+               mark_ce_used(next, o);
+       }
+}
+
+static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
+{
+       const struct index_state *index = o->src_index;
+       int pos = o->cache_bottom;
+
+       while (pos < index->cache_nr) {
+               struct cache_entry *ce = index->cache[pos];
+               if (!(ce->ce_flags & CE_UNPACKED))
+                       return ce;
+               pos++;
+       }
+       return NULL;
+}
+
+static void add_same_unmerged(struct cache_entry *ce,
+                             struct unpack_trees_options *o)
+{
+       struct index_state *index = o->src_index;
+       int len = ce_namelen(ce);
+       int pos = index_name_pos(index, ce->name, len);
+
+       if (0 <= pos)
+               die("programming error in a caller of mark_ce_used_same_name");
+       for (pos = -pos - 1; pos < index->cache_nr; pos++) {
+               struct cache_entry *next = index->cache[pos];
+               if (len != ce_namelen(next) ||
+                   memcmp(ce->name, next->name, len))
+                       break;
+               add_entry(o, next, 0, 0);
+               mark_ce_used(next, o);
+       }
+}
+
+static int unpack_index_entry(struct cache_entry *ce,
+                             struct unpack_trees_options *o)
 {
        struct cache_entry *src[5] = { ce, NULL, };
+       int ret;
 
-       o->pos++;
+       mark_ce_used(ce, o);
        if (ce_stage(ce)) {
                if (o->skip_unmerged) {
                        add_entry(o, ce, 0, 0);
                        return 0;
                }
        }
-       return call_unpack_fn(src, o);
+       ret = call_unpack_fn(src, o);
+       if (ce_stage(ce))
+               mark_ce_used_same_name(ce, o);
+       return ret;
+}
+
+static int find_cache_pos(struct traverse_info *, const struct name_entry *);
+
+static void restore_cache_bottom(struct traverse_info *info, int bottom)
+{
+       struct unpack_trees_options *o = info->data;
+
+       if (o->diff_index_cached)
+               return;
+       o->cache_bottom = bottom;
+}
+
+static int switch_cache_bottom(struct traverse_info *info)
+{
+       struct unpack_trees_options *o = info->data;
+       int ret, pos;
+
+       if (o->diff_index_cached)
+               return 0;
+       ret = o->cache_bottom;
+       pos = find_cache_pos(info->prev, &info->name);
+
+       if (pos < -1)
+               o->cache_bottom = -2 - pos;
+       else if (pos < 0)
+               o->cache_bottom = o->src_index->cache_nr;
+       return ret;
 }
 
 static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
 {
-       int i;
+       int i, ret, bottom;
        struct tree_desc t[MAX_UNPACK_TREES];
        struct traverse_info newinfo;
        struct name_entry *p;
@@ -235,7 +354,11 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long
                        sha1 = names[i].sha1;
                fill_tree_descriptor(t+i, sha1);
        }
-       return traverse_trees(n, t, &newinfo);
+
+       bottom = switch_cache_bottom(&newinfo);
+       ret = traverse_trees(n, t, &newinfo);
+       restore_cache_bottom(&newinfo, bottom);
+       return ret;
 }
 
 /*
@@ -284,6 +407,20 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf
        return ce_namelen(ce) > traverse_path_len(info, n);
 }
 
+static int ce_in_traverse_path(const struct cache_entry *ce,
+                              const struct traverse_info *info)
+{
+       if (!info->prev)
+               return 1;
+       if (do_compare_entry(ce, info->prev, &info->name))
+               return 0;
+       /*
+        * If ce (blob) is the same name as the path (which is a tree
+        * we will be descending into), it won't be inside it.
+        */
+       return (info->pathlen < ce_namelen(ce));
+}
+
 static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
 {
        int len = traverse_path_len(info, n);
@@ -360,6 +497,114 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message)
        return -1;
 }
 
+/* NEEDSWORK: give this a better name and share with tree-walk.c */
+static int name_compare(const char *a, int a_len,
+                       const char *b, int b_len)
+{
+       int len = (a_len < b_len) ? a_len : b_len;
+       int cmp = memcmp(a, b, len);
+       if (cmp)
+               return cmp;
+       return (a_len - b_len);
+}
+
+/*
+ * The tree traversal is looking at name p.  If we have a matching entry,
+ * return it.  If name p is a directory in the index, do not return
+ * anything, as we will want to match it when the traversal descends into
+ * the directory.
+ */
+static int find_cache_pos(struct traverse_info *info,
+                         const struct name_entry *p)
+{
+       int pos;
+       struct unpack_trees_options *o = info->data;
+       struct index_state *index = o->src_index;
+       int pfxlen = info->pathlen;
+       int p_len = tree_entry_len(p->path, p->sha1);
+
+       for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
+               struct cache_entry *ce = index->cache[pos];
+               const char *ce_name, *ce_slash;
+               int cmp, ce_len;
+
+               if (!ce_in_traverse_path(ce, info))
+                       continue;
+               if (ce->ce_flags & CE_UNPACKED)
+                       continue;
+               ce_name = ce->name + pfxlen;
+               ce_slash = strchr(ce_name, '/');
+               if (ce_slash)
+                       ce_len = ce_slash - ce_name;
+               else
+                       ce_len = ce_namelen(ce) - pfxlen;
+               cmp = name_compare(p->path, p_len, ce_name, ce_len);
+               /*
+                * Exact match; if we have a directory we need to
+                * delay returning it.
+                */
+               if (!cmp)
+                       return ce_slash ? -2 - pos : pos;
+               if (0 < cmp)
+                       continue; /* keep looking */
+               /*
+                * ce_name sorts after p->path; could it be that we
+                * have files under p->path directory in the index?
+                * E.g.  ce_name == "t-i", and p->path == "t"; we may
+                * have "t/a" in the index.
+                */
+               if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) &&
+                   ce_name[p_len] < '/')
+                       continue; /* keep looking */
+               break;
+       }
+       return -1;
+}
+
+static struct cache_entry *find_cache_entry(struct traverse_info *info,
+                                           const struct name_entry *p)
+{
+       int pos = find_cache_pos(info, p);
+       struct unpack_trees_options *o = info->data;
+
+       if (0 <= pos)
+               return o->src_index->cache[pos];
+       else
+               return NULL;
+}
+
+static void debug_path(struct traverse_info *info)
+{
+       if (info->prev) {
+               debug_path(info->prev);
+               if (*info->prev->name.path)
+                       putchar('/');
+       }
+       printf("%s", info->name.path);
+}
+
+static void debug_name_entry(int i, struct name_entry *n)
+{
+       printf("ent#%d %06o %s\n", i,
+              n->path ? n->mode : 0,
+              n->path ? n->path : "(missing)");
+}
+
+static void debug_unpack_callback(int n,
+                                 unsigned long mask,
+                                 unsigned long dirmask,
+                                 struct name_entry *names,
+                                 struct traverse_info *info)
+{
+       int i;
+       printf("* unpack mask %lu, dirmask %lu, cnt %d ",
+              mask, dirmask, n);
+       debug_path(info);
+       putchar('\n');
+       for (i = 0; i < n; i++)
+               debug_name_entry(i, names + i);
+}
+
 static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
 {
        struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@ -370,25 +615,38 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
        while (!p->mode)
                p++;
 
+       if (o->debug_unpack)
+               debug_unpack_callback(n, mask, dirmask, names, info);
+
        /* Are we supposed to look at the index too? */
        if (o->merge) {
-               while (o->pos < o->src_index->cache_nr) {
-                       struct cache_entry *ce = o->src_index->cache[o->pos];
-                       int cmp = compare_entry(ce, info, p);
+               while (1) {
+                       int cmp;
+                       struct cache_entry *ce;
+
+                       if (o->diff_index_cached)
+                               ce = next_cache_entry(o);
+                       else
+                               ce = find_cache_entry(info, p);
+
+                       if (!ce)
+                               break;
+                       cmp = compare_entry(ce, info, p);
                        if (cmp < 0) {
                                if (unpack_index_entry(ce, o) < 0)
                                        return unpack_failed(o, NULL);
                                continue;
                        }
                        if (!cmp) {
-                               o->pos++;
                                if (ce_stage(ce)) {
                                        /*
-                                        * If we skip unmerged index entries, we'll skip this
-                                        * entry *and* the tree entries associated with it!
+                                        * If we skip unmerged index
+                                        * entries, we'll skip this
+                                        * entry *and* the tree
+                                        * entries associated with it!
                                         */
                                        if (o->skip_unmerged) {
-                                               add_entry(o, ce, 0, 0);
+                                               add_same_unmerged(ce, o);
                                                return mask;
                                        }
                                }
@@ -401,6 +659,13 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
        if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
                return -1;
 
+       if (src[0]) {
+               if (ce_stage(src[0]))
+                       mark_ce_used_same_name(src[0], o);
+               else
+                       mark_ce_used(src[0], o);
+       }
+
        /* Now handle any directories.. */
        if (dirmask) {
                unsigned long conflicts = mask & ~dirmask;
@@ -417,11 +682,13 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
                        matches = cache_tree_matches_traversal(o->src_index->cache_tree,
                                                               names, info);
                        /*
-                        * Everything under the name matches.  Adjust o->pos to
-                        * skip the entire hierarchy.
+                        * Everything under the name matches; skip the
+                        * entire hierarchy.  diff_index_cached codepath
+                        * special cases D/F conflicts in such a way that
+                        * it does not do any look-ahead, so this is safe.
                         */
                        if (matches) {
-                               o->pos += matches;
+                               o->cache_bottom += matches;
                                return mask;
                        }
                }
@@ -465,11 +732,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
        memset(&o->result, 0, sizeof(o->result));
        o->result.initialized = 1;
-       if (o->src_index) {
-               o->result.timestamp.sec = o->src_index->timestamp.sec;
-               o->result.timestamp.nsec = o->src_index->timestamp.nsec;
-       }
+       o->result.timestamp.sec = o->src_index->timestamp.sec;
+       o->result.timestamp.nsec = o->src_index->timestamp.nsec;
        o->merge_size = len;
+       mark_all_ce_unused(o->src_index);
 
        if (!dfc)
                dfc = xcalloc(1, cache_entry_size(0));
@@ -483,22 +749,38 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                info.fn = unpack_callback;
                info.data = o;
 
-               if (traverse_trees(len, t, &info) < 0) {
-                       ret = unpack_failed(o, NULL);
-                       goto done;
+               if (o->prefix) {
+                       /*
+                        * Unpack existing index entries that sort before the
+                        * prefix the tree is spliced into.  Note that o->merge
+                        * is always true in this case.
+                        */
+                       while (1) {
+                               struct cache_entry *ce = next_cache_entry(o);
+                               if (!ce)
+                                       break;
+                               if (ce_in_traverse_path(ce, &info))
+                                       break;
+                               if (unpack_index_entry(ce, o) < 0)
+                                       goto return_failed;
+                       }
                }
+
+               if (traverse_trees(len, t, &info) < 0)
+                       goto return_failed;
        }
 
        /* Any left-over entries in the index? */
        if (o->merge) {
-               while (o->pos < o->src_index->cache_nr) {
-                       struct cache_entry *ce = o->src_index->cache[o->pos];
-                       if (unpack_index_entry(ce, o) < 0) {
-                               ret = unpack_failed(o, NULL);
-                               goto done;
-                       }
+               while (1) {
+                       struct cache_entry *ce = next_cache_entry(o);
+                       if (!ce)
+                               break;
+                       if (unpack_index_entry(ce, o) < 0)
+                               goto return_failed;
                }
        }
+       mark_all_ce_unused(o->src_index);
 
        if (o->trivial_merges_only && o->nontrivial_merge) {
                ret = unpack_failed(o, "Merge requires file-level merging");
@@ -543,6 +825,11 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                free(el.excludes);
 
        return ret;
+
+return_failed:
+       mark_all_ce_unused(o->src_index);
+       ret = unpack_failed(o, NULL);
+       goto done;
 }
 
 /* Here come the merge functions */
@@ -661,7 +948,9 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
         * in that directory.
         */
        namelen = strlen(ce->name);
-       for (i = o->pos; i < o->src_index->cache_nr; i++) {
+       for (i = locate_in_src_index(ce, o);
+            i < o->src_index->cache_nr;
+            i++) {
                struct cache_entry *ce2 = o->src_index->cache[i];
                int len = ce_namelen(ce2);
                if (len < namelen ||
@@ -669,12 +958,14 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
                    ce2->name[namelen] != '/')
                        break;
                /*
-                * ce2->name is an entry in the subdirectory.
+                * ce2->name is an entry in the subdirectory to be
+                * removed.
                 */
                if (!ce_stage(ce2)) {
                        if (verify_uptodate(ce2, o))
                                return -1;
                        add_entry(o, ce2, CE_REMOVE, 0);
+                       mark_ce_used(ce2, o);
                }
                cnt++;
        }
@@ -731,7 +1022,6 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,
                return 0;
 
        if (!lstat(ce->name, &st)) {
-               int ret;
                int dtype = ce_to_dtype(ce);
                struct cache_entry *result;
 
@@ -759,28 +1049,8 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,
                         * files that are in "foo/" we would lose
                         * them.
                         */
-                       ret = verify_clean_subdirectory(ce, action, o);
-                       if (ret < 0)
-                               return ret;
-
-                       /*
-                        * If this removed entries from the index,
-                        * what that means is:
-                        *
-                        * (1) the caller unpack_callback() saw path/foo
-                        * in the index, and it has not removed it because
-                        * it thinks it is handling 'path' as blob with
-                        * D/F conflict;
-                        * (2) we will return "ok, we placed a merged entry
-                        * in the index" which would cause o->pos to be
-                        * incremented by one;
-                        * (3) however, original o->pos now has 'path/foo'
-                        * marked with "to be removed".
-                        *
-                        * We need to increment it by the number of
-                        * deleted entries here.
-                        */
-                       o->pos += ret;
+                       if (verify_clean_subdirectory(ce, action, o) < 0)
+                               return -1;
                        return 0;
                }
 
@@ -927,7 +1197,8 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
                remote = NULL;
        }
 
-       /* First, if there's a #16 situation, note that to prevent #13
+       /*
+        * First, if there's a #16 situation, note that to prevent #13
         * and #14.
         */
        if (!same(remote, head)) {
@@ -941,7 +1212,8 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
                }
        }
 
-       /* We start with cases where the index is allowed to match
+       /*
+        * We start with cases where the index is allowed to match
         * something other than the head: #14(ALT) and #2ALT, where it
         * is permitted to match the result instead.
         */
@@ -971,12 +1243,13 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
        if (!head && !remote && any_anc_missing)
                return 0;
 
-       /* Under the new "aggressive" rule, we resolve mostly trivial
+       /*
+        * Under the "aggressive" rule, we resolve mostly trivial
         * cases that we historically had git-merge-one-file resolve.
         */
        if (o->aggressive) {
-               int head_deleted = !head && !df_conflict_head;
-               int remote_deleted = !remote && !df_conflict_remote;
+               int head_deleted = !head;
+               int remote_deleted = !remote;
                struct cache_entry *ce = NULL;
 
                if (index)
index 95ff36c824a14a24234459ad552306fb1b5dabf1..ef70eab39025fcdaccda059692ae447a13fa0aeb 100644 (file)
@@ -31,10 +31,11 @@ struct unpack_trees_options {
                     skip_unmerged,
                     initial_checkout,
                     diff_index_cached,
+                    debug_unpack,
                     skip_sparse_checkout,
                     gently;
        const char *prefix;
-       int pos;
+       int cache_bottom;
        struct dir_struct *dir;
        merge_fn_t fn;
        struct unpack_trees_error_msgs msgs;