Merge branch 'mg/maint-remote-fix' into maint
authorShawn O. Pearce <spearce@spearce.org>
Mon, 29 Sep 2008 16:39:53 +0000 (09:39 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Mon, 29 Sep 2008 16:39:53 +0000 (09:39 -0700)
* mg/maint-remote-fix:
  make "git remote" report multiple URLs

53 files changed:
Documentation/RelNotes-1.6.0.3.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-read-tree.txt
Documentation/git-repack.txt
Documentation/gitattributes.txt
Documentation/gitdiffcore.txt
RelNotes
builtin-apply.c
builtin-archive.c
builtin-checkout.c
builtin-clone.c
builtin-commit.c
builtin-diff-files.c
builtin-diff.c
builtin-for-each-ref.c
builtin-log.c
builtin-merge-recursive.c
builtin-merge.c
builtin-pack-objects.c
builtin-prune.c
builtin-read-tree.c
builtin-remote.c
builtin-rm.c
contrib/completion/git-completion.bash
contrib/hooks/setgitperms.perl
diff.c
diffcore.h
dir.c
dir.h
fast-import.c
git-rebase--interactive.sh
git-repack.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
gitweb/gitweb.perl
http.c
index-pack.c
pack-write.c
read-cache.c
sha1_file.c
t/lib-git-svn.sh
t/t0024-crlf-archive.sh [new file with mode: 0644]
t/t1300-repo-config.sh
t/t3030-merge-recursive.sh
t/t9113-git-svn-dcommit-new-file.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9126-git-svn-follow-deleted-readded-directory.sh [new file with mode: 0755]
t/t9126/follow-deleted-readded.dump [new file with mode: 0644]
templates/Makefile
unpack-trees.c
unpack-trees.h

diff --git a/Documentation/RelNotes-1.6.0.3.txt b/Documentation/RelNotes-1.6.0.3.txt
new file mode 100644 (file)
index 0000000..46e13a4
--- /dev/null
@@ -0,0 +1,45 @@
+GIT v1.6.0.3 Release Notes
+==========================
+
+Fixes since v1.6.0.2
+--------------------
+
+* "git archive --format=zip" did not honor core.autocrlf while
+  --format=tar did.
+
+* Continuing "git rebase -i" was very confused when the user left modified
+  files in the working tree while resolving conflicts.
+
+* Continuing "git rebase -i" was also very confused when the user left
+  some staged changes in the index after "edit".
+
+* Behaviour of "git diff --quiet" was inconsistent with "diff --exit-code"
+  with the output redirected to /dev/null.
+
+* "git stash apply sash@{1}" was fixed to error out.  Prior versions
+  would have applied stash@{0} incorrectly.
+
+* "git for-each-ref --format=%(subject)" fixed for commits with no
+  no newline in the message body.
+
+* "git remote" fixed to protect printf from user input.
+
+* "git checkout -q" once again suppresses the locally modified file list.
+
+* Cross-directory renames are no longer used when creating packs.  This
+  allows more graceful behavior on filesystems like sshfs.
+
+* Stale temporary files under $GIT_DIR/objects/pack are now cleaned up
+  automatically by "git prune".
+
+* "Git.pm" tests relied on unnecessarily more recent version of Perl.
+
+* "gitweb" triggered undef warning on commits without log messages.
+
+Many other documentation updates.
+
+--
+exec >/var/tmp/1
+O=v1.6.0.2-41-g7fe4a72
+echo O=$(git describe maint)
+git shortlog --no-merges $O..maint
index 3727239..87b028f 100644 (file)
@@ -363,8 +363,17 @@ core.pager::
        variable.  Note that git sets the `LESS` environment
        variable to `FRSX` if it is unset when it runs the
        pager.  One can change these settings by setting the
-       `LESS` variable to some other value or by giving the
-       `core.pager` option a value such as "`less -+FRSX`".
+       `LESS` variable to some other value.  Alternately,
+       these settings can be overridden on a project or
+       global basis by setting the `core.pager` option.
+       Setting `core.pager` has no affect on the `LESS`
+       environment variable behaviour above, so if you want
+       to override git's default settings this way, you need
+       to be explicit.  For example, to disable the S option
+       in a backward compatible manner, set `core.pager`
+       to "`less -+$LESS -FRX`".  This will be passed to the
+       shell by git, which will translate the final command to
+       "`LESS=FRSX less -+FRSX -FRX`".
 
 core.whitespace::
        A comma separated list of common whitespace problems to
index 6f4b9b0..309deac 100644 (file)
@@ -160,7 +160,10 @@ Here are the "carry forward" rules:
       0 nothing             nothing  nothing  (does not happen)
       1 nothing             nothing  exists   use M
       2 nothing             exists   nothing  remove path from index
-      3 nothing             exists   exists   use M
+      3 nothing             exists   exists,  use M if "initial checkout"
+                                    H == M   keep index otherwise
+                                    exists   fail
+                                    H != M
 
         clean I==H  I==M
        ------------------
@@ -207,6 +210,12 @@ you picked it up via e-mail in a patch form), `git diff-index
 merge, but it would not show in `git diff-index --cached $M`
 output after two-tree merge.
 
+Case #3 is slightly tricky and needs explanation.  The result from this
+rule logically should be to remove the path if the user staged the removal
+of the path and then swiching to a new branch.  That however will prevent
+the initial checkout from happening, so the rule is modified to use M (new
+tree) only when the contents of the index is empty.  Otherwise the removal
+of the path is kept as long as $H and $M are the same.
 
 3-Way Merge
 ~~~~~~~~~~~
index 38ac609..bbe1485 100644 (file)
@@ -60,7 +60,7 @@ OPTIONS
        linkgit:git-pack-objects[1].
 
 -f::
-       Pass the `--no-reuse-delta` option to 'git-pack-objects'. See
+       Pass the `--no-reuse-object` option to `git-pack-objects`, see
        linkgit:git-pack-objects[1].
 
 -q::
index 94e6752..8962768 100644 (file)
@@ -270,27 +270,27 @@ See linkgit:git[1] for details.
 Defining a custom hunk-header
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Each group of changes (called "hunk") in the textual diff output
+Each group of changes (called "hunk") in the textual diff output
 is prefixed with a line of the form:
 
        @@ -k,l +n,m @@ TEXT
 
-The text is called 'hunk header', and by default a line that
-begins with an alphabet, an underscore or a dollar sign is used,
-which matches what GNU 'diff -p' output uses.  This default
-selection however is not suited for some contents, and you can
-use customized pattern to make a selection.
+This is called a 'hunk header'.  The "TEXT" portion is by default a line
+that begins with an alphabet, an underscore or a dollar sign; this
+matches what GNU 'diff -p' output uses.  This default selection however
+is not suited for some contents, and you can use a customized pattern
+to make a selection.
 
-First in .gitattributes, you would assign the `diff` attribute
+First, in .gitattributes, you would assign the `diff` attribute
 for paths.
 
 ------------------------
 *.tex  diff=tex
 ------------------------
 
-Then, you would define "diff.tex.funcname" configuration to
+Then, you would define "diff.tex.funcname" configuration to
 specify a regular expression that matches a line that you would
-want to appear as the hunk header, like this:
+want to appear as the hunk header "TEXT", like this:
 
 ------------------------
 [diff "tex"]
index 2bdbc3d..e8041bc 100644 (file)
@@ -36,11 +36,25 @@ files:
 
  - 'git-diff-tree' compares contents of two "tree" objects;
 
-In all of these cases, the commands themselves compare
-corresponding paths in the two sets of files.  The result of
-comparison is passed from these commands to what is internally
-called "diffcore", in a format similar to what is output when
-the -p option is not used.  E.g.
+In all of these cases, the commands themselves first optionally limit
+the two sets of files by any pathspecs given on their command-lines,
+and compare corresponding paths in the two resulting sets of files.
+
+The pathspecs are used to limit the world diff operates in.  They remove
+the filepairs outside the specified sets of pathnames.  E.g. If the
+input set of filepairs included:
+
+------------------------------------------------
+:100644 100644 bcd1234... 0123456... M junkfile
+------------------------------------------------
+
+but the command invocation was `git diff-files myfile`, then the
+junkfile entry would be removed from the list because only "myfile"
+is under consideration.
+
+The result of comparison is passed from these commands to what is
+internally called "diffcore", in a format similar to what is output
+when the -p option is not used.  E.g.
 
 ------------------------------------------------
 in-place edit  :100644 100644 bcd1234... 0123456... M file0
@@ -52,9 +66,8 @@ unmerged       :000000 000000 0000000... 0000000... U file6
 The diffcore mechanism is fed a list of such comparison results
 (each of which is called "filepair", although at this point each
 of them talks about a single file), and transforms such a list
-into another list.  There are currently 6 such transformations:
+into another list.  There are currently 5 such transformations:
 
-- diffcore-pathspec
 - diffcore-break
 - diffcore-rename
 - diffcore-merge-broken
@@ -62,38 +75,14 @@ into another list.  There are currently 6 such transformations:
 - diffcore-order
 
 These are applied in sequence.  The set of filepairs 'git-diff-{asterisk}'
-commands find are used as the input to diffcore-pathspec, and
-the output from diffcore-pathspec is used as the input to the
+commands find are used as the input to diffcore-break, and
+the output from diffcore-break is used as the input to the
 next transformation.  The final result is then passed to the
 output routine and generates either diff-raw format (see Output
 format sections of the manual for 'git-diff-{asterisk}' commands) or
 diff-patch format.
 
 
-diffcore-pathspec: For Ignoring Files Outside Our Consideration
----------------------------------------------------------------
-
-The first transformation in the chain is diffcore-pathspec, and
-is controlled by giving the pathname parameters to the
-'git-diff-{asterisk}' commands on the command line.  The pathspec is used
-to limit the world diff operates in.  It removes the filepairs
-outside the specified set of pathnames.  E.g. If the input set
-of filepairs included:
-
-------------------------------------------------
-:100644 100644 bcd1234... 0123456... M junkfile
-------------------------------------------------
-
-but the command invocation was `git diff-files myfile`, then the
-junkfile entry would be removed from the list because only "myfile"
-is under consideration.
-
-Implementation note.  For performance reasons, 'git-diff-tree'
-uses the pathname parameters on the command line to cull set of
-filepairs it feeds the diffcore mechanism itself, and does not
-use diffcore-pathspec, but the end result is the same.
-
-
 diffcore-break: For Splitting Up "Complete Rewrites"
 ----------------------------------------------------
 
index 726bff6..a677737 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.0.2.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.0.3.txt
\ No newline at end of file
index 20bef1f..70c9f93 100644 (file)
@@ -13,6 +13,7 @@
 #include "delta.h"
 #include "builtin.h"
 #include "string-list.h"
+#include "dir.h"
 
 /*
  *  --check turns on checking that the working tree matches the
@@ -2735,15 +2736,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
                                warning("unable to remove submodule %s",
                                        patch->old_name);
                } else if (!unlink(patch->old_name) && rmdir_empty) {
-                       char *name = xstrdup(patch->old_name);
-                       char *end = strrchr(name, '/');
-                       while (end) {
-                               *end = 0;
-                               if (rmdir(name))
-                                       break;
-                               end = strrchr(name, '/');
-                       }
-                       free(name);
+                       remove_path(patch->old_name);
                }
        }
 }
index 5ceec43..432ce2a 100644 (file)
@@ -111,6 +111,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
 {
        const char *remote = NULL;
 
+       git_config(git_default_config, NULL);
+
        remote = extract_remote_arg(&argc, argv);
        if (remote)
                return run_remote_archiver(remote, argc, argv);
index 8e77767..c4fc2b2 100644 (file)
@@ -269,6 +269,8 @@ static int merge_working_tree(struct checkout_opts *opts,
                }
 
                /* 2-way merge to the new branch */
+               topts.initial_checkout = (!active_nr &&
+                                         (old->commit == new->commit));
                topts.update = 1;
                topts.merge = 1;
                topts.gently = opts->merge;
@@ -326,7 +328,7 @@ static int merge_working_tree(struct checkout_opts *opts,
            commit_locked_index(lock_file))
                die("unable to write new index file");
 
-       if (!opts->force)
+       if (!opts->force && !opts->quiet)
                show_local_changes(&new->commit->object);
 
        return 0;
index c843529..5b40e07 100644 (file)
@@ -58,7 +58,7 @@ static struct option builtin_clone_options[] = {
        OPT_STRING(0, "reference", &option_reference, "repo",
                   "reference repository"),
        OPT_STRING('o', "origin", &option_origin, "branch",
-                  "use <branch> instead or 'origin' to track upstream"),
+                  "use <branch> instead of 'origin' to track upstream"),
        OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
                   "path to git-upload-pack on the remote"),
        OPT_STRING(0, "depth", &option_depth, "depth",
index c870037..e2a7e48 100644 (file)
@@ -639,7 +639,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                active_cache_tree = cache_tree();
        if (cache_tree_update(active_cache_tree,
                              active_cache, active_nr, 0, 0) < 0) {
-               error("Error building trees");
+               error("Error building trees; the index is unmerged?");
                return 0;
        }
 
index 9bf10bb..2b578c7 100644 (file)
@@ -50,7 +50,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
            3 < rev.max_count)
                usage(diff_files_usage);
 
-       if (rev.max_count == -1 &&
+       /*
+        * "diff-files --base -p" should not combine merges because it
+        * was not asked to.  "diff-files -c -p" should not densify
+        * (the user should ask with "diff-files --cc" explicitly).
+        */
+       if (rev.max_count == -1 && !rev.combine_merges &&
            (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
                rev.combine_merges = rev.dense_combined_merges = 1;
 
index 037c303..d5fe775 100644 (file)
@@ -223,7 +223,13 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
                argv++; argc--;
        }
 
-       if (revs->max_count == -1 &&
+       /*
+        * "diff --base" should not combine merges because it was not
+        * asked to.  "diff -c" should not densify (if the user wants
+        * dense one, --cc can be explicitly asked for, or just rely
+        * on the default).
+        */
+       if (revs->max_count == -1 && !revs->combine_merges &&
            (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
                revs->combine_merges = revs->dense_combined_merges = 1;
 
index 21e92bb..72c0878 100644 (file)
@@ -320,9 +320,7 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un
 
 static const char *copy_line(const char *buf)
 {
-       const char *eol = strchr(buf, '\n');
-       if (!eol)
-               return "";
+       const char *eol = strchrnul(buf, '\n');
        return xmemdupz(buf, eol - buf);
 }
 
index 911fd65..2efe593 100644 (file)
@@ -835,7 +835,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
                        endpos = strchr(committer, '>');
                        if (!endpos)
-                               die("bogos committer info %s\n", committer);
+                               die("bogus committer info %s\n", committer);
                        add_signoff = xmemdupz(committer, endpos - committer + 1);
                }
                else if (!strcmp(argv[i], "--attach")) {
index 43e55bf..b973865 100644 (file)
@@ -18,6 +18,7 @@
 #include "ll-merge.h"
 #include "interpolate.h"
 #include "attr.h"
+#include "dir.h"
 #include "merge-recursive.h"
 
 static int subtree_merge;
@@ -416,24 +417,6 @@ static int update_stages(const char *path, struct diff_filespec *o,
        return 0;
 }
 
-static int remove_path(const char *name)
-{
-       int ret;
-       char *slash, *dirs;
-
-       ret = unlink(name);
-       if (ret)
-               return ret;
-       dirs = xstrdup(name);
-       while ((slash = strrchr(name, '/'))) {
-               *slash = '\0';
-               if (rmdir(name) != 0)
-                       break;
-       }
-       free(dirs);
-       return ret;
-}
-
 static int remove_file(int clean, const char *path, int no_wd)
 {
        int update_cache = index_only || clean;
@@ -444,10 +427,8 @@ static int remove_file(int clean, const char *path, int no_wd)
                        return -1;
        }
        if (update_working_directory) {
-               unlink(path);
-               if (errno != ENOENT || errno != EISDIR)
+               if (remove_path(path))
                        return -1;
-               remove_path(path);
        }
        return 0;
 }
index b280444..dcaf368 100644 (file)
@@ -442,6 +442,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
+               if (argc < 0)
+                       die("Bad branch.%s.mergeoptions string", branch);
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
index 217fd49..4004e73 100644 (file)
@@ -465,7 +465,7 @@ static void write_pack_file(void)
                        char tmpname[PATH_MAX];
                        int fd;
                        snprintf(tmpname, sizeof(tmpname),
-                                "%s/tmp_pack_XXXXXX", get_object_directory());
+                                "%s/pack/tmp_pack_XXXXXX", get_object_directory());
                        fd = xmkstemp(tmpname);
                        pack_tmp_name = xstrdup(tmpname);
                        f = sha1fd(fd, pack_tmp_name);
index c767a0a..1663f8b 100644 (file)
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 static int show_only;
 static unsigned long expire;
 
-static int prune_tmp_object(char *path, const char *filename)
+static int prune_tmp_object(const char *path, const char *filename)
 {
        const char *fullpath = mkpath("%s/%s", path, filename);
        if (expire) {
@@ -110,24 +110,22 @@ static void prune_object_dir(const char *path)
 /*
  * Write errors (particularly out of space) can result in
  * failed temporary packs (and more rarely indexes and other
- * files begining with "tmp_") accumulating in the
- * object directory.
+ * files begining with "tmp_") accumulating in the object
+ * and the pack directories.
  */
-static void remove_temporary_files(void)
+static void remove_temporary_files(const char *path)
 {
        DIR *dir;
        struct dirent *de;
-       char* dirname=get_object_directory();
 
-       dir = opendir(dirname);
+       dir = opendir(path);
        if (!dir) {
-               fprintf(stderr, "Unable to open object directory %s\n",
-                       dirname);
+               fprintf(stderr, "Unable to open directory %s\n", path);
                return;
        }
        while ((de = readdir(dir)) != NULL)
                if (!prefixcmp(de->d_name, "tmp_"))
-                       prune_tmp_object(dirname, de->d_name);
+                       prune_tmp_object(path, de->d_name);
        closedir(dir);
 }
 
@@ -141,6 +139,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
                         "expire objects older than <time>"),
                OPT_END()
        };
+       char *s;
 
        save_commit_buffer = 0;
        init_revisions(&revs, prefix);
@@ -163,6 +162,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        prune_object_dir(get_object_directory());
 
        prune_packed_objects(show_only);
-       remove_temporary_files();
+       remove_temporary_files(get_object_directory());
+       s = xstrdup(mkpath("%s/pack", get_object_directory()));
+       remove_temporary_files(s);
+       free(s);
        return 0;
 }
index ac219ac..0706c95 100644 (file)
@@ -206,6 +206,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                        break;
                case 2:
                        opts.fn = twoway_merge;
+                       opts.initial_checkout = !active_nr;
                        break;
                case 3:
                default:
index 1e2edc2..90a4e35 100644 (file)
@@ -407,14 +407,15 @@ static int rm(int argc, const char **argv)
        return i;
 }
 
-static void show_list(const char *title, struct string_list *list)
+static void show_list(const char *title, struct string_list *list,
+                     const char *extra_arg)
 {
        int i;
 
        if (!list->nr)
                return;
 
-       printf(title, list->nr > 1 ? "es" : "");
+       printf(title, list->nr > 1 ? "es" : "", extra_arg);
        printf("\n    ");
        for (i = 0; i < list->nr; i++)
                printf("%s%s", i ? " " : "", list->items[i].string);
@@ -477,7 +478,6 @@ static int show(int argc, const char **argv)
 
        memset(&states, 0, sizeof(states));
        for (; argc; argc--, argv++) {
-               struct strbuf buf;
                int i;
 
                get_remote_ref_states(*argv, &states, !no_query);
@@ -503,18 +503,16 @@ static int show(int argc, const char **argv)
                }
 
                if (!no_query) {
-                       strbuf_init(&buf, 0);
-                       strbuf_addf(&buf, "  New remote branch%%s (next fetch "
-                               "will store in remotes/%s)", states.remote->name);
-                       show_list(buf.buf, &states.new);
-                       strbuf_release(&buf);
+                       show_list("  New remote branch%s (next fetch "
+                               "will store in remotes/%s)",
+                               &states.new, states.remote->name);
                        show_list("  Stale tracking branch%s (use 'git remote "
-                               "prune')", &states.stale);
+                               "prune')", &states.stale, "");
                }
 
                if (no_query)
                        for_each_ref(append_ref_to_tracked_list, &states);
-               show_list("  Tracked remote branch%s", &states.tracked);
+               show_list("  Tracked remote branch%s", &states.tracked, "");
 
                if (states.remote->push_refspec_nr) {
                        printf("  Local branch%s pushed with 'git push'\n   ",
index fdac34f..50ae6d5 100644 (file)
@@ -29,26 +29,6 @@ static void add_list(const char *name)
        list.name[list.nr++] = name;
 }
 
-static int remove_file(const char *name)
-{
-       int ret;
-       char *slash;
-
-       ret = unlink(name);
-       if (ret && errno == ENOENT)
-               /* The user has removed it from the filesystem by hand */
-               ret = errno = 0;
-
-       if (!ret && (slash = strrchr(name, '/'))) {
-               char *n = xstrdup(name);
-               do {
-                       n[slash - name] = 0;
-                       name = n;
-               } while (!rmdir(name) && (slash = strrchr(name, '/')));
-       }
-       return ret;
-}
-
 static int check_local_mod(unsigned char *head, int index_only)
 {
        /* items in list are already sorted in the cache order,
@@ -239,7 +219,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                int removed = 0;
                for (i = 0; i < list.nr; i++) {
                        const char *path = list.name[i];
-                       if (!remove_file(path)) {
+                       if (!remove_path(path)) {
                                removed = 1;
                                continue;
                        }
index 1154ae1..3bc45f6 100755 (executable)
@@ -750,7 +750,7 @@ _git_commit ()
        --*)
                __gitcomp "
                        --all --author= --signoff --verify --no-verify
-                       --edit --amend --include --only
+                       --edit --amend --include --only --interactive
                        "
                return
        esac
index dab7c8e..a577ad0 100644 (file)
@@ -50,7 +50,7 @@ if ((@ARGV < 0) || !GetOptions(
                              )) { die $usage; }
 die $usage unless ($read_mode xor $write_mode);
 
-my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir;
+my $topdir = `git rev-parse --show-cdup` or die "\n"; chomp $topdir;
 my $gitdir = $topdir . '.git';
 my $gitmeta = $topdir . '.gitmeta';
 
@@ -155,7 +155,7 @@ elsif ($read_mode) {
        open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n";
     }
 
-    my @files = `git-ls-files`;
+    my @files = `git ls-files`;
     my %dirs;
 
     foreach my $path (@files) {
diff --git a/diff.c b/diff.c
index 5e01b2b..9385a36 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2400,13 +2400,6 @@ int diff_setup_done(struct diff_options *options)
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
 
-       /*
-        * If we postprocess in diffcore, we cannot simply return
-        * upon the first hit.  We need to run diff as usual.
-        */
-       if (options->pickaxe || options->filter)
-               DIFF_OPT_CLR(options, QUIET);
-
        return 0;
 }
 
@@ -3398,10 +3391,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
 
 void diffcore_std(struct diff_options *options)
 {
-       if (DIFF_OPT_TST(options, QUIET))
-               return;
-
-       if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+       if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);
index cc96c20..8ae3578 100644 (file)
@@ -92,7 +92,6 @@ extern struct diff_filepair *diff_queue(struct diff_queue_struct *,
                                        struct diff_filespec *);
 extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);
 
-extern void diffcore_pathspec(const char **pathspec);
 extern void diffcore_break(int);
 extern void diffcore_rename(struct diff_options *);
 extern void diffcore_merge_broken(void);
diff --git a/dir.c b/dir.c
index 109e05b..cfaa28f 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -837,3 +837,23 @@ void setup_standard_excludes(struct dir_struct *dir)
        if (excludes_file && !access(excludes_file, R_OK))
                add_excludes_from_file(dir, excludes_file);
 }
+
+int remove_path(const char *name)
+{
+       char *slash;
+
+       if (unlink(name) && errno != ENOENT)
+               return -1;
+
+       slash = strrchr(name, '/');
+       if (slash) {
+               char *dirs = xstrdup(name);
+               slash = dirs + (slash - name);
+               do {
+                       *slash = '\0';
+               } while (rmdir(dirs) && (slash = strrchr(dirs, '/')));
+               free(dirs);
+       }
+       return 0;
+}
+
diff --git a/dir.h b/dir.h
index 2df15de..278ee42 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -81,4 +81,7 @@ extern int is_inside_dir(const char *dir);
 extern void setup_standard_excludes(struct dir_struct *dir);
 extern int remove_dir_recursively(struct strbuf *path, int only_empty);
 
+/* tries to remove the path with empty directories along it, ignores ENOENT */
+extern int remove_path(const char *path);
+
 #endif
index d85b3a5..5473cd4 100644 (file)
@@ -816,7 +816,7 @@ static void start_packfile(void)
        int pack_fd;
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/tmp_pack_XXXXXX", get_object_directory());
+               "%s/pack/tmp_pack_XXXXXX", get_object_directory());
        pack_fd = xmkstemp(tmpfile);
        p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
        strcpy(p->pack_name, tmpfile);
@@ -878,7 +878,7 @@ static char *create_index(void)
        }
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/tmp_idx_XXXXXX", get_object_directory());
+               "%s/pack/tmp_idx_XXXXXX", get_object_directory());
        idx_fd = xmkstemp(tmpfile);
        f = sha1fd(idx_fd, tmpfile);
        sha1write(f, array, 256 * sizeof(int));
index 929d681..edb6ec6 100755 (executable)
@@ -284,7 +284,7 @@ do_next () {
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
                make_patch $sha1
-               : > "$DOTEST"/amend
+               git rev-parse --verify HEAD > "$DOTEST"/amend
                warn "Stopped at $sha1... $rest"
                warn "You can amend the commit now, with"
                warn
@@ -427,14 +427,22 @@ do
                else
                        . "$DOTEST"/author-script ||
                                die "Cannot find the author identity"
+                       amend=
                        if test -f "$DOTEST"/amend
                        then
+                               amend=$(git rev-parse --verify HEAD)
+                               test "$amend" = $(cat "$DOTEST"/amend) ||
+                               die "\
+You have uncommitted changes in your working tree. Please, commit them
+first and then run 'git rebase --continue' again."
                                git reset --soft HEAD^ ||
                                die "Cannot rewind the HEAD"
                        fi
                        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
-                       git commit --no-verify -F "$DOTEST"/message -e ||
-                       die "Could not commit staged changes."
+                       git commit --no-verify -F "$DOTEST"/message -e || {
+                               test -n "$amend" && git reset --soft $amend
+                               die "Could not commit staged changes."
+                       }
                fi
 
                require_clean_work_tree
index 683960b..d39eb6c 100755 (executable)
@@ -10,7 +10,7 @@ git repack [options]
 a               pack everything in a single pack
 A               same as -a, and turn unreachable objects loose
 d               remove redundant packs, and run git-prune-packed
-f               pass --no-reuse-delta to git-pack-objects
+f               pass --no-reuse-object to git-pack-objects
 n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
index d799c76..42f626f 100755 (executable)
@@ -144,7 +144,14 @@ show_stash () {
        then
                flags=--stat
        fi
-       s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@")
+
+       if test $# = 0
+       then
+               set x "$ref_stash@{0}"
+               shift
+       fi
+
+       s=$(git rev-parse --revs-only --no-flags "$@")
 
        w_commit=$(git rev-parse --verify "$s") &&
        b_commit=$(git rev-parse --verify "$s^") &&
@@ -154,7 +161,7 @@ show_stash () {
 apply_stash () {
        git update-index -q --refresh &&
        git diff-files --quiet --ignore-submodules ||
-               die 'Cannot restore on top of a dirty state'
+               die 'Cannot apply to a dirty working tree, please stage your changes'
 
        unstash_index=
        case "$1" in
@@ -163,13 +170,19 @@ apply_stash () {
                shift
        esac
 
+       if test $# = 0
+       then
+               set x "$ref_stash@{0}"
+               shift
+       fi
+
        # current index state
        c_tree=$(git write-tree) ||
                die 'Cannot apply a stash in the middle of a merge'
 
        # stash records the work tree, and is a merge between the
        # base commit (first parent) and the index tree (second parent).
-       s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
+       s=$(git rev-parse --revs-only --no-flags "$@") &&
        w_tree=$(git rev-parse --verify "$s:") &&
        b_tree=$(git rev-parse --verify "$s^1:") &&
        i_tree=$(git rev-parse --verify "$s^2:") ||
index b40f876..5888735 100755 (executable)
@@ -185,7 +185,7 @@ cmd_add()
        else
 
                module_clone "$path" "$realrepo" || exit
-               (unset GIT_DIR; cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
+               (unset GIT_DIR; cd "$path" && git checkout -f -q ${branch:+-b "$branch" "origin/$branch"}) ||
                die "Unable to checkout submodule '$path'"
        fi
 
@@ -311,8 +311,13 @@ cmd_update()
 
                if test "$subsha1" != "$sha1"
                then
+                       force=
+                       if test -z "$subsha1"
+                       then
+                               force="-f"
+                       fi
                        (unset GIT_DIR; cd "$path" && git-fetch &&
-                               git-checkout -q "$sha1") ||
+                               git-checkout $force -q "$sha1") ||
                        die "Unable to checkout '$sha1' in submodule path '$path'"
 
                        say "Submodule path '$path': checked out '$sha1'"
index 237895c..7c7fc39 100755 (executable)
@@ -3969,21 +3969,21 @@ sub gs_do_switch {
        my $old_url = $full_url;
        $full_url .= '/' . escape_uri_only($path) if length $path;
        my ($ra, $reparented);
-       if ($old_url ne $full_url) {
-               if ($old_url !~ m#^svn(\+ssh)?://#) {
-                       SVN::_Ra::svn_ra_reparent($self->{session}, $full_url,
-                                                 $pool);
-                       $self->{url} = $full_url;
-                       $reparented = 1;
-               } else {
-                       $_[0] = undef;
-                       $self = undef;
-                       $RA = undef;
-                       $ra = Git::SVN::Ra->new($full_url);
-                       $ra_invalid = 1;
-               }
+
+       if ($old_url =~ m#^svn(\+ssh)?://#) {
+               $_[0] = undef;
+               $self = undef;
+               $RA = undef;
+               $ra = Git::SVN::Ra->new($full_url);
+               $ra_invalid = 1;
+       } elsif ($old_url ne $full_url) {
+               SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool);
+               $self->{url} = $full_url;
+               $reparented = 1;
        }
+
        $ra ||= $self;
+       $url_b = escape_url($url_b);
        my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool);
        my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
        $reporter->set_path('', $rev_a, 0, @lock, $pool);
diff --git a/git.c b/git.c
index fdb0f71..5582c51 100644 (file)
--- a/git.c
+++ b/git.c
@@ -162,6 +162,8 @@ static int handle_alias(int *argcp, const char ***argv)
                            alias_string + 1, alias_command);
                }
                count = split_cmdline(alias_string, &new_argv);
+               if (count < 0)
+                       die("Bad alias.%s string", alias_command);
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
                        die("alias '%s' changes environment variables\n"
index 90cd99b..269f112 100755 (executable)
@@ -2092,7 +2092,7 @@ sub parse_commit_text {
                        last;
                }
        }
-       if ($co{'title'} eq "") {
+       if (! defined $co{'title'} || $co{'title'} eq "") {
                $co{'title'} = $co{'title_short'} = '(no commit message)';
        }
        # remove added spaces
diff --git a/http.c b/http.c
index 1108ab4..a97fdf5 100644 (file)
--- a/http.c
+++ b/http.c
@@ -165,7 +165,16 @@ static CURL* get_curl_handle(void)
 {
        CURL* result = curl_easy_init();
 
-       curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);
+       if (!curl_ssl_verify) {
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
+       } else {
+               /* Verify authenticity of the peer's certificate */
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
+               /* The name in the cert must match whom we tried to connect */
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
+       }
+
 #if LIBCURL_VERSION_NUM >= 0x070907
        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 #endif
index a6e91fe..530d820 100644 (file)
@@ -172,7 +172,7 @@ static char *open_pack_file(char *pack_name)
                if (!pack_name) {
                        static char tmpfile[PATH_MAX];
                        snprintf(tmpfile, sizeof(tmpfile),
-                                "%s/tmp_pack_XXXXXX", get_object_directory());
+                                "%s/pack/tmp_pack_XXXXXX", get_object_directory());
                        output_fd = xmkstemp(tmpfile);
                        pack_name = xstrdup(tmpfile);
                } else
index 939ed56..3621f1d 100644 (file)
@@ -45,7 +45,7 @@ char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
        if (!index_name) {
                static char tmpfile[PATH_MAX];
                snprintf(tmpfile, sizeof(tmpfile),
-                        "%s/tmp_idx_XXXXXX", get_object_directory());
+                        "%s/pack/tmp_idx_XXXXXX", get_object_directory());
                fd = xmkstemp(tmpfile);
                index_name = xstrdup(tmpfile);
        } else {
index 35fec46..8f96fd1 100644 (file)
@@ -1244,6 +1244,7 @@ int discard_index(struct index_state *istate)
        istate->cache_nr = 0;
        istate->cache_changed = 0;
        istate->timestamp = 0;
+       istate->name_hash_initialized = 0;
        free_hash(&istate->name_hash);
        cache_tree_free(&(istate->cache_tree));
        free(istate->alloc);
index 477d3fb..e2cb342 100644 (file)
@@ -2136,7 +2136,9 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
  */
 int move_temp_to_file(const char *tmpfile, const char *filename)
 {
-       int ret = link(tmpfile, filename);
+       int ret = 0;
+       if (link(tmpfile, filename))
+               ret = errno;
 
        /*
         * Coda hack - coda doesn't like cross-directory links,
index a841df2..5b5f288 100644 (file)
@@ -135,3 +135,20 @@ close $wr or die $!;
 close $rd or die $!;
 EOF
 }
+
+require_svnserve () {
+    if test -z "$SVNSERVE_PORT"
+    then
+        say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
+        test_done
+        exit
+    fi
+}
+
+start_svnserve () {
+    svnserve --listen-port $SVNSERVE_PORT \
+             --root "$rawsvnrepo" \
+             --listen-once \
+             --listen-host 127.0.0.1 &
+}
+
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
new file mode 100644 (file)
index 0000000..e533039
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='respect crlf in git archive'
+
+. ./test-lib.sh
+UNZIP=${UNZIP:-unzip}
+
+test_expect_success setup '
+
+       git config core.autocrlf true
+
+       printf "CRLF line ending\r\nAnd another\r\n" > sample &&
+       git add sample &&
+
+       test_tick &&
+       git commit -m Initial
+
+'
+
+test_expect_success 'tar archive' '
+
+       git archive --format=tar HEAD |
+       ( mkdir untarred && cd untarred && "$TAR" -xf - )
+
+       test_cmp sample untarred/sample
+
+'
+
+"$UNZIP" -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+       echo "Skipping ZIP test, because unzip was not found"
+       test_done
+       exit
+fi
+
+test_expect_success 'zip archive' '
+
+       git archive --format=zip HEAD >test.zip &&
+
+       ( mkdir unzipped && cd unzipped && unzip ../test.zip ) &&
+
+       test_cmp sample unzipped/sample
+
+'
+
+test_done
index 64567fb..11b82f4 100755 (executable)
@@ -741,4 +741,14 @@ test_expect_success 'symlinked configuration' '
 
 '
 
+test_expect_success 'check split_cmdline return' "
+       git config alias.split-cmdline-fix 'echo \"' &&
+       test_must_fail git split-cmdline-fix &&
+       echo foo > foo &&
+       git add foo &&
+       git commit -m 'initial commit' &&
+       git config branch.master.mergeoptions 'echo \"' &&
+       test_must_fail git merge master
+       "
+
 test_done
index de0cdb1..0de613d 100755 (executable)
@@ -535,4 +535,15 @@ test_expect_success 'reset and bind merge' '
 
 '
 
+test_expect_success 'merge removes empty directories' '
+
+       git reset --hard master &&
+       git checkout -b rm &&
+       git rm d/e &&
+       git commit -mremoved-d/e &&
+       git checkout master &&
+       git merge -s recursive rm &&
+       test_must_fail test -d d
+'
+
 test_done
index ae78e33..c2b24a4 100755 (executable)
@@ -12,19 +12,7 @@ test_description='git-svn dcommit new files over svn:// test'
 
 . ./lib-git-svn.sh
 
-if test -z "$SVNSERVE_PORT"
-then
-       say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
-       test_done
-       exit
-fi
-
-start_svnserve () {
-       svnserve --listen-port $SVNSERVE_PORT \
-                --root "$rawsvnrepo" \
-                --listen-once \
-                --listen-host 127.0.0.1 &
-}
+require_svnserve
 
 test_expect_success 'start tracking an empty repo' '
        svn mkdir -m "empty dir" "$svnrepo"/empty-dir &&
index 3281cbd..43ceb75 100755 (executable)
@@ -6,6 +6,10 @@
 test_description='git-svn funky branch names'
 . ./lib-git-svn.sh
 
+# Abo-Uebernahme (Bug #994)
+scary_uri='Abo-Uebernahme%20%28Bug%20%23994%29'
+scary_ref='Abo-Uebernahme%20(Bug%20#994)'
+
 test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches project/tags &&
        echo foo > project/trunk/foo &&
@@ -15,6 +19,8 @@ test_expect_success 'setup svnrepo' '
                        "$svnrepo/pr ject/branches/fun plugin" &&
        svn cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \
                              "$svnrepo/pr ject/branches/more fun plugin!" &&
+       svn cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \
+                     "$svnrepo/pr ject/branches/$scary_uri" &&
        start_httpd
        '
 
@@ -23,6 +29,7 @@ test_expect_success 'test clone with funky branch names' '
        cd project &&
                git rev-parse "refs/remotes/fun%20plugin" &&
                git rev-parse "refs/remotes/more%20fun%20plugin!" &&
+               git rev-parse "refs/remotes/$scary_ref" &&
        cd ..
        '
 
@@ -35,6 +42,15 @@ test_expect_success 'test dcommit to funky branch' "
        cd ..
        "
 
+test_expect_success 'test dcommit to scary branch' '
+       cd project &&
+       git reset --hard "refs/remotes/$scary_ref" &&
+       echo urls are scary >> foo &&
+       git commit -m "eep" -- foo &&
+       git svn dcommit &&
+       cd ..
+       '
+
 stop_httpd
 
 test_done
diff --git a/t/t9126-git-svn-follow-deleted-readded-directory.sh b/t/t9126-git-svn-follow-deleted-readded-directory.sh
new file mode 100755 (executable)
index 0000000..edec640
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Alec Berryman
+
+test_description='git svn fetch repository with deleted and readded directory'
+
+. ./lib-git-svn.sh
+
+# Don't run this by default; it opens up a port.
+require_svnserve
+
+test_expect_success 'load repository' '
+    svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9126/follow-deleted-readded.dump
+    '
+
+test_expect_success 'fetch repository' '
+    start_svnserve &&
+    git svn init svn://127.0.0.1:$SVNSERVE_PORT &&
+    git svn fetch
+    '
+
+test_done
diff --git a/t/t9126/follow-deleted-readded.dump b/t/t9126/follow-deleted-readded.dump
new file mode 100644 (file)
index 0000000..19da5d1
--- /dev/null
@@ -0,0 +1,201 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 1807dc6f-c693-4cda-9710-00e1be8c1f21
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-09-14T19:53:13.006748Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 12
+Create trunk
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:13.239689Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 119
+Content-length: 119
+
+K 7
+svn:log
+V 20
+Create trunk/project
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:13.548860Z
+PROPS-END
+
+Node-path: trunk/project
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 3
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 12
+add new file
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:15.433630Z
+PROPS-END
+
+Node-path: trunk/project/foo
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Revision-number: 4
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 17
+change foo to bar
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:17.339884Z
+PROPS-END
+
+Node-path: trunk/project/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: c157a79031e1c40f85931829bc5fc552
+Content-length: 4
+
+bar
+
+
+Revision-number: 5
+Prop-content-length: 114
+Content-length: 114
+
+K 7
+svn:log
+V 15
+don't like that
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:19.335001Z
+PROPS-END
+
+Node-path: trunk/project
+Node-action: delete
+
+
+Revision-number: 6
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 11
+reset trunk
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:19.845897Z
+PROPS-END
+
+Node-path: trunk/project
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 4
+Node-copyfrom-path: trunk/project
+
+
+Revision-number: 7
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 14
+change to quux
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:21.367947Z
+PROPS-END
+
+Node-path: trunk/project/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 5
+Text-content-md5: d3b07a382ec010c01889250fce66fb13
+Content-length: 5
+
+quux
+
+
index cc3fc30..a12c6e2 100644 (file)
@@ -23,17 +23,19 @@ all: boilerplates.made custom
 
 bpsrc = $(filter-out %~,$(wildcard *--*))
 boilerplates.made : $(bpsrc)
-       $(QUIET)ls *--* 2>/dev/null | \
+       $(QUIET)umask 022 && ls *--* 2>/dev/null | \
        while read boilerplate; \
        do \
                case "$$boilerplate" in *~) continue ;; esac && \
                dst=`echo "$$boilerplate" | sed -e 's|^this|.|;s|--|/|g'` && \
                dir=`expr "$$dst" : '\(.*\)/'` && \
-               $(INSTALL) -d -m 755 blt/$$dir && \
+               mkdir -p blt/$$dir && \
                case "$$boilerplate" in \
-               *--) ;; \
-               *) cp -p $$boilerplate blt/$$dst ;; \
-               esac || exit; \
+               *--) continue;; \
+               esac && \
+               cp $$boilerplate blt/$$dst && \
+               if test -x "blt/$$dst"; then rx=rx; else rx=r; fi && \
+               chmod a+$$rx "blt/$$dst" || exit; \
        done && \
        date >$@
 
index ef21c62..e59d144 100644 (file)
@@ -941,8 +941,17 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
                        return -1;
                }
        }
-       else if (newtree)
+       else if (newtree) {
+               if (oldtree && !o->initial_checkout) {
+                       /*
+                        * deletion of the path was staged;
+                        */
+                       if (same(oldtree, newtree))
+                               return 1;
+                       return reject_merge(oldtree, o);
+               }
                return merged_entry(newtree, current, o);
+       }
        return deleted_entry(oldtree, current, o);
 }
 
index 94e5672..0d26f3d 100644 (file)
@@ -26,6 +26,7 @@ struct unpack_trees_options {
                     verbose_update:1,
                     aggressive:1,
                     skip_unmerged:1,
+                    initial_checkout:1,
                     gently:1;
        const char *prefix;
        int pos;