Merge branch 'gl/web'
authorJunio C Hamano <junkio@cox.net>
Mon, 28 Aug 2006 23:20:28 +0000 (16:20 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 28 Aug 2006 23:20:28 +0000 (16:20 -0700)
* gl/web: (46 commits)
gitweb: Use @diff_opts, default ('M'), as git-diff and git-diff-tree paramete
gitweb: Remove git_to_hash function
gitweb: Remove unused git_get_{preceding,following}_references
gitweb: Fix typo in git_patchset_body
gitweb: Fix typo in git_difftree_body
gitweb: blobs defined by non-textual hash ids can be cached
gitweb: Improve comments about gitweb features configuration
gitweb: Remove workaround for git-diff bug fixed in f82cd3c
gitweb: Remove creating directory for temporary files
gitweb: Remove git_diff_print subroutine
gitweb: git_blobdiff_plain is git_blobdiff('plain')
gitweb: Use git-diff-tree or git-diff patch output for blobdiff
gitweb: Change here-doc back for style consistency in git_blobdiff
gitweb: Always display link to blobdiff_plain in git_blobdiff
gitweb: Add invisible hyperlink to from-file/to-file diff header
gitweb: Parse two-line from-file/to-file diff header in git_patchset_body
gitweb: Allow for pre-parsed difftree info in git_patchset_body
gitweb: Add support for hash_parent_base parameter for blobdiffs
gitweb: Use git_get_name_rev_tags for commitdiff_plain X-Git-Tag: header
gitweb: Add git_get_rev_name_tags function
...

44 files changed:
.gitignore
Documentation/git-apply.txt
Documentation/git-daemon.txt
Documentation/git-zip-tree.txt [new file with mode: 0644]
Makefile
builtin-apply.c
builtin-cat-file.c
builtin-checkout-index.c
builtin-fmt-merge-msg.c
builtin-log.c
builtin-mv.c
builtin-repo-config.c
builtin-rev-list.c
builtin-zip-tree.c [new file with mode: 0644]
builtin.h
cache.h
commit.c
config.c
daemon.c
date.c
diff-delta.c
diff.c
dir.c
entry.c
fetch.c
git-cherry.sh
git-compat-util.h
git-merge.sh
git-rebase.sh
git-reset.sh
git-svn.perl
git.c
http-fetch.c
http-push.c
merge-recursive.c [new file with mode: 0644]
object-refs.c
object.c
path-list.c
read-cache.c
refs.c
sha1_file.c
t/t4116-apply-reverse.sh
t/t4117-apply-reject.sh [new file with mode: 0755]
xdiff-interface.c
index 55cd9844d61e202670076968b20dcb0fda68732e..78cb6715398eabcd070e5301798b935f5db62565 100644 (file)
@@ -62,6 +62,7 @@ git-merge-tree
 git-merge-octopus
 git-merge-one-file
 git-merge-ours
+git-merge-recur
 git-merge-recursive
 git-merge-resolve
 git-merge-stupid
@@ -124,6 +125,7 @@ git-verify-pack
 git-verify-tag
 git-whatchanged
 git-write-tree
+git-zip-tree
 git-core-*/?*
 gitweb/gitweb.cgi
 test-date
index 20e12ceda002d929ffc7ee13b77d0e23c7e91356..c76cfffdc6cd9982aa02e34b2b09fc22897e7402 100644 (file)
@@ -75,8 +75,8 @@ OPTIONS
        For atomicity, gitlink:git-apply[1] by default fails the whole patch and
        does not touch the working tree when some of the hunks
        do not apply.  This option makes it apply
-       the parts of the patch that are applicable, and send the
-       rejected hunks to the standard output of the command.
+       the parts of the patch that are applicable, and leave the
+       rejected hunks in corresponding *.rej files.
 
 -z::
        When showing the index information, do not munge paths,
index 0f7d274eaba3722dabfdbf7ab14592a938933a56..17619a3f57c939f63a5648b166b52263565e6cb2 100644 (file)
@@ -11,7 +11,8 @@ SYNOPSIS
 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
              [--timeout=n] [--init-timeout=n] [--strict-paths]
              [--base-path=path] [--user-path | --user-path=path]
-            [--reuseaddr] [--detach] [--pid-file=file] [directory...]
+            [--reuseaddr] [--detach] [--pid-file=file]
+            [--user=user [--group=group]] [directory...]
 
 DESCRIPTION
 -----------
@@ -93,6 +94,17 @@ OPTIONS
 --pid-file=file::
        Save the process id in 'file'.
 
+--user=user, --group=group::
+       Change daemon's uid and gid before entering the service loop.
+       When only `--user` is given without `--group`, the
+       primary group ID for the user is used.  The values of
+       the option are given to `getpwnam(3)` and `getgrnam(3)`
+       and numeric IDs are not supported.
++
+Giving these options is an error when used with `--inetd`; use
+the facility of inet daemon to achieve the same before spawning
+`git-daemon` if needed.
+
 <directory>::
        A directory to add to the whitelist of allowed directories. Unless
        --strict-paths is specified this will also include subdirectories
diff --git a/Documentation/git-zip-tree.txt b/Documentation/git-zip-tree.txt
new file mode 100644 (file)
index 0000000..2e9d981
--- /dev/null
@@ -0,0 +1,67 @@
+git-zip-tree(1)
+===============
+
+NAME
+----
+git-zip-tree - Creates a ZIP archive of the files in the named tree
+
+
+SYNOPSIS
+--------
+'git-zip-tree' [-0|...|-9] <tree-ish> [ <base> ]
+
+DESCRIPTION
+-----------
+Creates a ZIP archive containing the tree structure for the named tree.
+When <base> is specified it is added as a leading path to the files in the
+generated ZIP archive.
+
+git-zip-tree behaves differently when given a tree ID versus when given
+a commit ID or tag ID.  In the first case the current time is used as
+modification time of each file in the archive.  In the latter case the
+commit time as recorded in the referenced commit object is used instead.
+Additionally the commit ID is stored as an archive comment.
+
+Currently git-zip-tree can handle only files and directories, symbolic
+links are not supported.
+
+OPTIONS
+-------
+
+-0::
+       Store the files instead of deflating them.
+
+-9::
+       Highest and slowest compression level.  You can specify any
+       number from 1 to 9 to adjust compression speed and ratio.
+
+<tree-ish>::
+       The tree or commit to produce ZIP archive for.  If it is
+       the object name of a commit object.
+
+<base>::
+       Leading path to the files in the resulting ZIP archive.
+
+EXAMPLES
+--------
+git zip-tree v1.4.0 git-1.4.0 >git-1.4.0.zip::
+
+       Create a ZIP file for v1.4.0 release.
+
+git zip-tree HEAD:Documentation/ git-docs >docs.zip::
+
+       Put everything in the current head's Documentation/ directory
+       into 'docs.zip', with the prefix 'git-docs/'.
+
+Author
+------
+Written by Rene Scharfe.
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
index b15b420ea218b23a53548577bdc874f8484a6d78..a6084764058d8713f2932df7f797d89b6113fd17 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -196,6 +196,7 @@ PROGRAMS = \
        git-upload-pack$X git-verify-pack$X \
        git-pack-redundant$X git-var$X \
        git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
+       git-merge-recur$X \
        $(EXTRA_PROGRAMS)
 
 # Empty...
@@ -292,7 +293,8 @@ BUILTIN_OBJS = \
        builtin-update-ref.o \
        builtin-upload-tar.o \
        builtin-verify-pack.o \
-       builtin-write-tree.o
+       builtin-write-tree.o \
+       builtin-zip-tree.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
@@ -710,6 +712,11 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
+merge-recursive.o path-list.o: path-list.h
+git-merge-recur$X: merge-recursive.o path-list.o $(GITLIBS)
+       $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+               $(LIBS)
+
 $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
 $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 $(DIFF_OBJS): diffcore.h
@@ -869,7 +876,7 @@ check-docs::
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
-               git-merge-resolve | git-merge-stupid | \
+               git-merge-resolve | git-merge-stupid | git-merge-recur | \
                git-ssh-pull | git-ssh-push ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
index f8f5eebd2f9b6ebbad46e8d86efde535f30a194f..1a1deaf78c61547a53bf0c3f7f1e7f37464610c5 100644 (file)
@@ -38,12 +38,14 @@ static int summary;
 static int check;
 static int apply = 1;
 static int apply_in_reverse;
+static int apply_with_reject;
+static int apply_verbosely;
 static int no_add;
 static int show_index_info;
 static int line_termination = '\n';
 static unsigned long p_context = -1;
 static const char apply_usage[] =
-"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
+"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
 
 static enum whitespace_eol {
        nowarn_whitespace,
@@ -122,6 +124,7 @@ struct fragment {
        unsigned long newpos, newlines;
        const char *patch;
        int size;
+       int rejected;
        struct fragment *next;
 };
 
@@ -138,6 +141,7 @@ struct patch {
        char *new_name, *old_name, *def_name;
        unsigned int old_mode, new_mode;
        int is_rename, is_copy, is_new, is_delete, is_binary;
+       int rejected;
        unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
@@ -150,6 +154,24 @@ struct patch {
        struct patch *next;
 };
 
+static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
+{
+       fputs(pre, output);
+       if (patch->old_name && patch->new_name &&
+           strcmp(patch->old_name, patch->new_name)) {
+               write_name_quoted(NULL, 0, patch->old_name, 1, output);
+               fputs(" => ", output);
+               write_name_quoted(NULL, 0, patch->new_name, 1, output);
+       }
+       else {
+               const char *n = patch->new_name;
+               if (!n)
+                       n = patch->old_name;
+               write_name_quoted(NULL, 0, n, 1, output);
+       }
+       fputs(post, output);
+}
+
 #define CHUNKSIZE (8192)
 #define SLOP (16)
 
@@ -1061,8 +1083,12 @@ static struct fragment *parse_binary_hunk(char **buf_p,
                llen = linelen(buffer, size);
                used += llen;
                linenr++;
-               if (llen == 1)
+               if (llen == 1) {
+                       /* consume the blank line */
+                       buffer++;
+                       size--;
                        break;
+               }
                /* Minimum line is "A00000\n" which is 7-byte long,
                 * and the line length must be multiple of 5 plus 2.
                 */
@@ -1105,8 +1131,7 @@ static struct fragment *parse_binary_hunk(char **buf_p,
        return frag;
 
  corrupt:
-       if (data)
-               free(data);
+       free(data);
        *status_p = -1;
        error("corrupt binary patch at line %d: %.*s",
              linenr-1, llen-1, buffer);
@@ -1303,8 +1328,7 @@ static void show_stats(struct patch *patch)
                printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
                       len, name, patch->lines_added + patch->lines_deleted,
                       add, pluses, del, minuses);
-       if (qname)
-               free(qname);
+       free(qname);
 }
 
 static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
@@ -1542,7 +1566,8 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
        lines = 0;
        pos = frag->newpos;
        for (;;) {
-               offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
+               offset = find_offset(buf, desc->size,
+                                    oldlines, oldsize, pos, &lines);
                if (match_end && offset + oldsize != desc->size)
                        offset = -1;
                if (match_beginning && offset)
@@ -1555,8 +1580,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                        /* Warn if it was necessary to reduce the number
                         * of context lines.
                         */
-                       if ((leading != frag->leading) || (trailing != frag->trailing))
-                               fprintf(stderr, "Context reduced to (%ld/%ld) to apply fragment at %d\n",
+                       if ((leading != frag->leading) ||
+                           (trailing != frag->trailing))
+                               fprintf(stderr, "Context reduced to (%ld/%ld)"
+                                       " to apply fragment at %d\n",
                                        leading, trailing, pos + lines);
 
                        if (size > alloc) {
@@ -1566,7 +1593,9 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                desc->buffer = buf;
                        }
                        desc->size = size;
-                       memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+                       memmove(buf + offset + newsize,
+                               buf + offset + oldsize,
+                               size - offset - newsize);
                        memcpy(buf + offset, newlines, newsize);
                        offset = 0;
 
@@ -1616,7 +1645,7 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
                                     "without the reverse hunk to '%s'",
                                     patch->new_name
                                     ? patch->new_name : patch->old_name);
-               fragment = fragment;
+               fragment = fragment->next;
        }
        data = (void*) fragment->patch;
        switch (fragment->binary_patch_method) {
@@ -1715,7 +1744,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
                                        sha1, hdr, &hdrlen);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
-                       return error("binary patch to '%s' creates incorrect result", name);
+                       return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
 
        return 0;
@@ -1730,9 +1759,12 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
                return apply_binary(desc, patch);
 
        while (frag) {
-               if (apply_one_fragment(desc, frag, patch->inaccurate_eof) < 0)
-                       return error("patch failed: %s:%ld",
-                                    name, frag->oldpos);
+               if (apply_one_fragment(desc, frag, patch->inaccurate_eof)) {
+                       error("patch failed: %s:%ld", name, frag->oldpos);
+                       if (!apply_with_reject)
+                               return -1;
+                       frag->rejected = 1;
+               }
                frag = frag->next;
        }
        return 0;
@@ -1768,8 +1800,9 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        desc.size = size;
        desc.alloc = alloc;
        desc.buffer = buf;
+
        if (apply_fragments(&desc, patch) < 0)
-               return -1;
+               return -1; /* note with --reject this succeeds. */
 
        /* NUL terminate the result */
        if (desc.alloc <= desc.size)
@@ -1794,6 +1827,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
        struct cache_entry *ce = NULL;
        int ok_if_exists;
 
+       patch->rejected = 1; /* we will drop this after we succeed */
        if (old_name) {
                int changed = 0;
                int stat_ret = 0;
@@ -1899,6 +1933,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 
        if (apply_data(patch, &st, ce) < 0)
                return error("%s: patch does not apply", name);
+       patch->rejected = 0;
        return 0;
 }
 
@@ -1908,6 +1943,9 @@ static int check_patch_list(struct patch *patch)
        int err = 0;
 
        for (prev_patch = NULL; patch ; patch = patch->next) {
+               if (apply_verbosely)
+                       say_patch_name(stderr,
+                                      "Checking patch ", patch, "...\n");
                err |= check_patch(patch, prev_patch);
                prev_patch = patch;
        }
@@ -2217,23 +2255,99 @@ static void write_out_one_result(struct patch *patch, int phase)
        if (phase == 0)
                remove_file(patch);
        if (phase == 1)
-       create_file(patch);
+               create_file(patch);
 }
 
-static void write_out_results(struct patch *list, int skipped_patch)
+static int write_out_one_reject(struct patch *patch)
+{
+       FILE *rej;
+       char namebuf[PATH_MAX];
+       struct fragment *frag;
+       int cnt = 0;
+
+       for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
+               if (!frag->rejected)
+                       continue;
+               cnt++;
+       }
+
+       if (!cnt) {
+               if (apply_verbosely)
+                       say_patch_name(stderr,
+                                      "Applied patch ", patch, " cleanly.\n");
+               return 0;
+       }
+
+       /* This should not happen, because a removal patch that leaves
+        * contents are marked "rejected" at the patch level.
+        */
+       if (!patch->new_name)
+               die("internal error");
+
+       /* Say this even without --verbose */
+       say_patch_name(stderr, "Applying patch ", patch, " with");
+       fprintf(stderr, " %d rejects...\n", cnt);
+
+       cnt = strlen(patch->new_name);
+       if (ARRAY_SIZE(namebuf) <= cnt + 5) {
+               cnt = ARRAY_SIZE(namebuf) - 5;
+               fprintf(stderr,
+                       "warning: truncating .rej filename to %.*s.rej",
+                       cnt - 1, patch->new_name);
+       }
+       memcpy(namebuf, patch->new_name, cnt);
+       memcpy(namebuf + cnt, ".rej", 5);
+
+       rej = fopen(namebuf, "w");
+       if (!rej)
+               return error("cannot open %s: %s", namebuf, strerror(errno));
+
+       /* Normal git tools never deal with .rej, so do not pretend
+        * this is a git patch by saying --git nor give extended
+        * headers.  While at it, maybe please "kompare" that wants
+        * the trailing TAB and some garbage at the end of line ;-).
+        */
+       fprintf(rej, "diff a/%s b/%s\t(rejected hunks)\n",
+               patch->new_name, patch->new_name);
+       for (cnt = 1, frag = patch->fragments;
+            frag;
+            cnt++, frag = frag->next) {
+               if (!frag->rejected) {
+                       fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
+                       continue;
+               }
+               fprintf(stderr, "Rejected hunk #%d.\n", cnt);
+               fprintf(rej, "%.*s", frag->size, frag->patch);
+               if (frag->patch[frag->size-1] != '\n')
+                       fputc('\n', rej);
+       }
+       fclose(rej);
+       return -1;
+}
+
+static int write_out_results(struct patch *list, int skipped_patch)
 {
        int phase;
+       int errs = 0;
+       struct patch *l;
 
        if (!list && !skipped_patch)
-               die("No changes");
+               return error("No changes");
 
        for (phase = 0; phase < 2; phase++) {
-               struct patch *l = list;
+               l = list;
                while (l) {
-                       write_out_one_result(l, phase);
+                       if (l->rejected)
+                               errs = 1;
+                       else {
+                               write_out_one_result(l, phase);
+                               if (phase == 1 && write_out_one_reject(l))
+                                       errs = 1;
+                       }
                        l = l->next;
                }
        }
+       return errs;
 }
 
 static struct lock_file lock_file;
@@ -2308,11 +2422,13 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
                        die("unable to read index file");
        }
 
-       if ((check || apply) && check_patch_list(list) < 0)
+       if ((check || apply) &&
+           check_patch_list(list) < 0 &&
+           !apply_with_reject)
                exit(1);
 
-       if (apply)
-               write_out_results(list, skipped_patch);
+       if (apply && write_out_results(list, skipped_patch))
+               exit(1);
 
        if (show_index_info)
                show_index_list(list);
@@ -2345,6 +2461,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
        int i;
        int read_stdin = 1;
        int inaccurate_eof = 0;
+       int errs = 0;
 
        const char *whitespace_option = NULL;
 
@@ -2354,7 +2471,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                int fd;
 
                if (!strcmp(arg, "-")) {
-                       apply_patch(0, "<stdin>", inaccurate_eof);
+                       errs |= apply_patch(0, "<stdin>", inaccurate_eof);
                        read_stdin = 0;
                        continue;
                }
@@ -2435,6 +2552,14 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        apply_in_reverse = 1;
                        continue;
                }
+               if (!strcmp(arg, "--reject")) {
+                       apply = apply_with_reject = apply_verbosely = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--verbose")) {
+                       apply_verbosely = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--inaccurate-eof")) {
                        inaccurate_eof = 1;
                        continue;
@@ -2455,18 +2580,19 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        usage(apply_usage);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
-               apply_patch(fd, arg, inaccurate_eof);
+               errs |= apply_patch(fd, arg, inaccurate_eof);
                close(fd);
        }
        set_default_whitespace_mode(whitespace_option);
        if (read_stdin)
-               apply_patch(0, "<stdin>", inaccurate_eof);
+               errs |= apply_patch(0, "<stdin>", inaccurate_eof);
        if (whitespace_error) {
                if (squelch_whitespace_errors &&
                    squelch_whitespace_errors < whitespace_error) {
                        int squelched =
                                whitespace_error - squelch_whitespace_errors;
-                       fprintf(stderr, "warning: squelched %d whitespace error%s\n",
+                       fprintf(stderr, "warning: squelched %d "
+                               "whitespace error%s\n",
                                squelched,
                                squelched == 1 ? "" : "s");
                }
@@ -2494,5 +2620,5 @@ int cmd_apply(int argc, const char **argv, const char *prefix)
                        die("Unable to write new index file");
        }
 
-       return 0;
+       return !!errs;
 }
index 7a6fa56e93034f0553e5a119180b3841cc773cb4..6c16bfa1ae4ae6207a74a8caa95564c649c4b0dd 100644 (file)
@@ -54,7 +54,7 @@ static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
                                        write_or_die(1, tagger, sp - tagger);
                                        date = strtoul(sp, &ep, 10);
                                        tz = strtol(ep, NULL, 10);
-                                       sp = show_date(date, tz);
+                                       sp = show_date(date, tz, 0);
                                        write_or_die(1, sp, strlen(sp));
                                        xwrite(1, "\n", 1);
                                        break;
index 6b55f931cbd8b79a01057b55eb41a043a4db5e3b..b097c888a0da044d6dc78c9747eb18e45b46709d 100644 (file)
@@ -45,7 +45,7 @@
 static int line_termination = '\n';
 static int checkout_stage; /* default to checkout stage0 */
 static int to_tempfile;
-static char topath[4][MAXPATHLEN+1];
+static char topath[4][PATH_MAX + 1];
 
 static struct checkout state;
 
index 28b5dfd054ea8277379d6913b1f1c4cde225e70d..76d22b47ba2906ecb59f0d8e279cf55a80bf8c86 100644 (file)
@@ -27,8 +27,8 @@ static void append_to_list(struct list *list, char *value, void *payload)
 {
        if (list->nr == list->alloc) {
                list->alloc += 32;
-               list->list = realloc(list->list, sizeof(char *) * list->alloc);
-               list->payload = realloc(list->payload,
+               list->list = xrealloc(list->list, sizeof(char *) * list->alloc);
+               list->payload = xrealloc(list->payload,
                                sizeof(char *) * list->alloc);
        }
        list->payload[list->nr] = payload;
@@ -55,8 +55,7 @@ static void free_list(struct list *list)
 
        for (i = 0; i < list->nr; i++) {
                free(list->list[i]);
-               if (list->payload[i])
-                       free(list->payload[i]);
+               free(list->payload[i]);
        }
        free(list->list);
        free(list->payload);
index 691cf3aef785950132c6be65f8aab39d4b12207e..fbc58bbcab1a992efb6ee359171d7ed6f0c52ab9 100644 (file)
@@ -101,7 +101,7 @@ static int git_format_config(const char *var, const char *value)
        if (!strcmp(var, "format.headers")) {
                int len = strlen(value);
                extra_headers_size += len + 1;
-               extra_headers = realloc(extra_headers, extra_headers_size);
+               extra_headers = xrealloc(extra_headers, extra_headers_size);
                extra_headers[extra_headers_size - len - 1] = 0;
                strcat(extra_headers, value);
                return 0;
@@ -381,7 +381,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        continue;
 
                nr++;
-               list = realloc(list, nr * sizeof(list[0]));
+               list = xrealloc(list, nr * sizeof(list[0]));
                list[nr - 1] = commit;
        }
        total = nr;
index fd1e52098174be212c51b957280f38dc6e9c0e48..4d21d88412cac52cbd5d127de439abc3e172b352 100644 (file)
@@ -168,13 +168,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                int j, dst_len;
 
                                if (last - first > 0) {
-                                       source = realloc(source,
+                                       source = xrealloc(source,
                                                        (count + last - first)
                                                        * sizeof(char *));
-                                       destination = realloc(destination,
+                                       destination = xrealloc(destination,
                                                        (count + last - first)
                                                        * sizeof(char *));
-                                       modes = realloc(modes,
+                                       modes = xrealloc(modes,
                                                        (count + last - first)
                                                        * sizeof(enum update_mode));
                                }
index c4164802081e03c8061f77498e08dd2ca8501af7..6560cf1c2dd04a1ef047767a69a68bac794d7187 100644 (file)
@@ -122,10 +122,8 @@ static int get_value(const char* key_, const char* regex_)
                ret =  (seen == 1) ? 0 : 1;
 
 free_strings:
-       if (repo_config)
-               free(repo_config);
-       if (global)
-               free(global);
+       free(repo_config);
+       free(global);
        return ret;
 }
 
index bc48a3e23081264d06f2659181a19b31694dd993..7f3e1fcfb34810285815f97979e8078e2ad6209d 100644 (file)
@@ -93,10 +93,8 @@ static void show_commit(struct commit *commit)
                free_commit_list(commit->parents);
                commit->parents = NULL;
        }
-       if (commit->buffer) {
-               free(commit->buffer);
-               commit->buffer = NULL;
-       }
+       free(commit->buffer);
+       commit->buffer = NULL;
 }
 
 static void process_blob(struct blob *blob,
diff --git a/builtin-zip-tree.c b/builtin-zip-tree.c
new file mode 100644 (file)
index 0000000..a5b834d
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2006 Rene Scharfe
+ */
+#include <time.h>
+#include "cache.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree.h"
+#include "quote.h"
+#include "builtin.h"
+
+static const char zip_tree_usage[] =
+"git-zip-tree [-0|...|-9] <tree-ish> [ <base> ]";
+
+static int zip_date;
+static int zip_time;
+
+static unsigned char *zip_dir;
+static unsigned int zip_dir_size;
+
+static unsigned int zip_offset;
+static unsigned int zip_dir_offset;
+static unsigned int zip_dir_entries;
+
+#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
+
+struct zip_local_header {
+       unsigned char magic[4];
+       unsigned char version[2];
+       unsigned char flags[2];
+       unsigned char compression_method[2];
+       unsigned char mtime[2];
+       unsigned char mdate[2];
+       unsigned char crc32[4];
+       unsigned char compressed_size[4];
+       unsigned char size[4];
+       unsigned char filename_length[2];
+       unsigned char extra_length[2];
+};
+
+struct zip_dir_header {
+       unsigned char magic[4];
+       unsigned char creator_version[2];
+       unsigned char version[2];
+       unsigned char flags[2];
+       unsigned char compression_method[2];
+       unsigned char mtime[2];
+       unsigned char mdate[2];
+       unsigned char crc32[4];
+       unsigned char compressed_size[4];
+       unsigned char size[4];
+       unsigned char filename_length[2];
+       unsigned char extra_length[2];
+       unsigned char comment_length[2];
+       unsigned char disk[2];
+       unsigned char attr1[2];
+       unsigned char attr2[4];
+       unsigned char offset[4];
+};
+
+struct zip_dir_trailer {
+       unsigned char magic[4];
+       unsigned char disk[2];
+       unsigned char directory_start_disk[2];
+       unsigned char entries_on_this_disk[2];
+       unsigned char entries[2];
+       unsigned char size[4];
+       unsigned char offset[4];
+       unsigned char comment_length[2];
+};
+
+static void copy_le16(unsigned char *dest, unsigned int n)
+{
+       dest[0] = 0xff & n;
+       dest[1] = 0xff & (n >> 010);
+}
+
+static void copy_le32(unsigned char *dest, unsigned int n)
+{
+       dest[0] = 0xff & n;
+       dest[1] = 0xff & (n >> 010);
+       dest[2] = 0xff & (n >> 020);
+       dest[3] = 0xff & (n >> 030);
+}
+
+static void *zlib_deflate(void *data, unsigned long size,
+                          unsigned long *compressed_size)
+{
+       z_stream stream;
+       unsigned long maxsize;
+       void *buffer;
+       int result;
+
+       memset(&stream, 0, sizeof(stream));
+       deflateInit(&stream, zlib_compression_level);
+       maxsize = deflateBound(&stream, size);
+       buffer = xmalloc(maxsize);
+
+       stream.next_in = data;
+       stream.avail_in = size;
+       stream.next_out = buffer;
+       stream.avail_out = maxsize;
+
+       do {
+               result = deflate(&stream, Z_FINISH);
+       } while (result == Z_OK);
+
+       if (result != Z_STREAM_END) {
+               free(buffer);
+               return NULL;
+       }
+
+       deflateEnd(&stream);
+       *compressed_size = stream.total_out;
+
+       return buffer;
+}
+
+static char *construct_path(const char *base, int baselen,
+                            const char *filename, int isdir, int *pathlen)
+{
+       int filenamelen = strlen(filename);
+       int len = baselen + filenamelen;
+       char *path, *p;
+
+       if (isdir)
+               len++;
+       p = path = xmalloc(len + 1);
+
+       memcpy(p, base, baselen);
+       p += baselen;
+       memcpy(p, filename, filenamelen);
+       p += filenamelen;
+       if (isdir)
+               *p++ = '/';
+       *p = '\0';
+
+       *pathlen = len;
+
+       return path;
+}
+
+static int write_zip_entry(const unsigned char *sha1,
+                           const char *base, int baselen,
+                           const char *filename, unsigned mode, int stage)
+{
+       struct zip_local_header header;
+       struct zip_dir_header dirent;
+       unsigned long compressed_size;
+       unsigned long uncompressed_size;
+       unsigned long crc;
+       unsigned long direntsize;
+       unsigned long size;
+       int method;
+       int result = -1;
+       int pathlen;
+       unsigned char *out;
+       char *path;
+       char type[20];
+       void *buffer = NULL;
+       void *deflated = NULL;
+
+       crc = crc32(0, Z_NULL, 0);
+
+       path = construct_path(base, baselen, filename, S_ISDIR(mode), &pathlen);
+       if (pathlen > 0xffff) {
+               error("path too long (%d chars, SHA1: %s): %s", pathlen,
+                     sha1_to_hex(sha1), path);
+               goto out;
+       }
+
+       if (S_ISDIR(mode)) {
+               method = 0;
+               result = READ_TREE_RECURSIVE;
+               out = NULL;
+               uncompressed_size = 0;
+               compressed_size = 0;
+       } else if (S_ISREG(mode)) {
+               method = zlib_compression_level == 0 ? 0 : 8;
+               result = 0;
+               buffer = read_sha1_file(sha1, type, &size);
+               if (!buffer)
+                       die("cannot read %s", sha1_to_hex(sha1));
+               crc = crc32(crc, buffer, size);
+               out = buffer;
+               uncompressed_size = size;
+               compressed_size = size;
+       } else {
+               error("unsupported file mode: 0%o (SHA1: %s)", mode,
+                     sha1_to_hex(sha1));
+               goto out;
+       }
+
+       if (method == 8) {
+               deflated = zlib_deflate(buffer, size, &compressed_size);
+               if (deflated && compressed_size - 6 < size) {
+                       /* ZLIB --> raw compressed data (see RFC 1950) */
+                       /* CMF and FLG ... */
+                       out = (unsigned char *)deflated + 2;
+                       compressed_size -= 6;   /* ... and ADLER32 */
+               } else {
+                       method = 0;
+                       compressed_size = size;
+               }
+       }
+
+       /* make sure we have enough free space in the dictionary */
+       direntsize = sizeof(struct zip_dir_header) + pathlen;
+       while (zip_dir_size < zip_dir_offset + direntsize) {
+               zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
+               zip_dir = xrealloc(zip_dir, zip_dir_size);
+       }
+
+       copy_le32(dirent.magic, 0x02014b50);
+       copy_le16(dirent.creator_version, 0);
+       copy_le16(dirent.version, 20);
+       copy_le16(dirent.flags, 0);
+       copy_le16(dirent.compression_method, method);
+       copy_le16(dirent.mtime, zip_time);
+       copy_le16(dirent.mdate, zip_date);
+       copy_le32(dirent.crc32, crc);
+       copy_le32(dirent.compressed_size, compressed_size);
+       copy_le32(dirent.size, uncompressed_size);
+       copy_le16(dirent.filename_length, pathlen);
+       copy_le16(dirent.extra_length, 0);
+       copy_le16(dirent.comment_length, 0);
+       copy_le16(dirent.disk, 0);
+       copy_le16(dirent.attr1, 0);
+       copy_le32(dirent.attr2, 0);
+       copy_le32(dirent.offset, zip_offset);
+       memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
+       zip_dir_offset += sizeof(struct zip_dir_header);
+       memcpy(zip_dir + zip_dir_offset, path, pathlen);
+       zip_dir_offset += pathlen;
+       zip_dir_entries++;
+
+       copy_le32(header.magic, 0x04034b50);
+       copy_le16(header.version, 20);
+       copy_le16(header.flags, 0);
+       copy_le16(header.compression_method, method);
+       copy_le16(header.mtime, zip_time);
+       copy_le16(header.mdate, zip_date);
+       copy_le32(header.crc32, crc);
+       copy_le32(header.compressed_size, compressed_size);
+       copy_le32(header.size, uncompressed_size);
+       copy_le16(header.filename_length, pathlen);
+       copy_le16(header.extra_length, 0);
+       write_or_die(1, &header, sizeof(struct zip_local_header));
+       zip_offset += sizeof(struct zip_local_header);
+       write_or_die(1, path, pathlen);
+       zip_offset += pathlen;
+       if (compressed_size > 0) {
+               write_or_die(1, out, compressed_size);
+               zip_offset += compressed_size;
+       }
+
+out:
+       free(buffer);
+       free(deflated);
+       free(path);
+
+       return result;
+}
+
+static void write_zip_trailer(const unsigned char *sha1)
+{
+       struct zip_dir_trailer trailer;
+
+       copy_le32(trailer.magic, 0x06054b50);
+       copy_le16(trailer.disk, 0);
+       copy_le16(trailer.directory_start_disk, 0);
+       copy_le16(trailer.entries_on_this_disk, zip_dir_entries);
+       copy_le16(trailer.entries, zip_dir_entries);
+       copy_le32(trailer.size, zip_dir_offset);
+       copy_le32(trailer.offset, zip_offset);
+       copy_le16(trailer.comment_length, sha1 ? 40 : 0);
+
+       write_or_die(1, zip_dir, zip_dir_offset);
+       write_or_die(1, &trailer, sizeof(struct zip_dir_trailer));
+       if (sha1)
+               write_or_die(1, sha1_to_hex(sha1), 40);
+}
+
+static void dos_time(time_t *time, int *dos_date, int *dos_time)
+{
+       struct tm *t = localtime(time);
+
+       *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
+                   (t->tm_year + 1900 - 1980) * 512;
+       *dos_time = t->tm_sec / 2 + t->tm_min * 32 + t->tm_hour * 2048;
+}
+
+int cmd_zip_tree(int argc, const char **argv, const char *prefix)
+{
+       unsigned char sha1[20];
+       struct tree *tree;
+       struct commit *commit;
+       time_t archive_time;
+       char *base;
+       int baselen;
+
+       git_config(git_default_config);
+
+       if (argc > 1 && argv[1][0] == '-') {
+               if (isdigit(argv[1][1]) && argv[1][2] == '\0') {
+                       zlib_compression_level = argv[1][1] - '0';
+                       argc--;
+                       argv++;
+               }
+       }
+
+       switch (argc) {
+       case 3:
+               base = strdup(argv[2]);
+               baselen = strlen(base);
+               break;
+       case 2:
+               base = strdup("");
+               baselen = 0;
+               break;
+       default:
+               usage(zip_tree_usage);
+       }
+
+       if (get_sha1(argv[1], sha1))
+               die("Not a valid object name %s", argv[1]);
+
+       commit = lookup_commit_reference_gently(sha1, 1);
+       archive_time = commit ? commit->date : time(NULL);
+       dos_time(&archive_time, &zip_date, &zip_time);
+
+       zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
+       zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
+
+       tree = parse_tree_indirect(sha1);
+       if (!tree)
+               die("not a tree object");
+
+       if (baselen > 0) {
+               write_zip_entry(tree->object.sha1, "", 0, base, 040777, 0);
+               base = xrealloc(base, baselen + 1);
+               base[baselen] = '/';
+               baselen++;
+               base[baselen] = '\0';
+       }
+       read_tree_recursive(tree, base, baselen, 0, NULL, write_zip_entry);
+       write_zip_trailer(commit ? commit->object.sha1 : NULL);
+
+       free(zip_dir);
+       free(base);
+
+       return 0;
+}
index ade58c4a1f06b161f00caa015192fa89c137d273..25431d70816634cec4ca6c54dc670ac536246277 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -52,6 +52,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_zip_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_update_index(int argc, const char **argv, const char *prefix);
 extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 1f212d77a4e41009c88c4b599022295b8a12a609..03d9dd04883f0d1d5c79436d598d667fe680c693 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -145,6 +145,7 @@ extern void verify_non_filename(const char *prefix, const char *name);
 extern int read_cache(void);
 extern int read_cache_from(const char *path);
 extern int write_cache(int newfd, struct cache_entry **cache, int entries);
+extern int discard_cache(void);
 extern int verify_path(const char *path);
 extern int cache_name_pos(const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
@@ -286,7 +287,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
                                        unsigned long *size,
                                        unsigned char *sha1_ret);
 
-const char *show_date(unsigned long time, int timezone);
+const char *show_date(unsigned long time, int timezone, int relative);
 const char *show_rfc2822_date(unsigned long time, int timezone);
 int parse_date(const char *date, char *buf, int bufsize);
 void datestamp(char *buf, int bufsize);
index 00bc3de22eb670c07e78087d96d9a3cff1d0fd67..c3ff9b4175c00a600ae25f9dc90eb521d7053719 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -507,14 +507,14 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c
        }
        switch (fmt) {
        case CMIT_FMT_MEDIUM:
-               ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
+               ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz, 0));
                break;
        case CMIT_FMT_EMAIL:
                ret += sprintf(buf + ret, "Date: %s\n",
                               show_rfc2822_date(time, tz));
                break;
        case CMIT_FMT_FULLER:
-               ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
+               ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz, 0));
                break;
        default:
                /* notin' */
index 82b35624543a2e50d80cafab15b63df0152adbc0..d9f2b787b94f2506ff7842c1b7b06bd15342ba6f 100644 (file)
--- a/config.c
+++ b/config.c
@@ -361,8 +361,7 @@ int git_config(config_fn_t fn)
        }
 
        ret += git_config_from_file(fn, filename);
-       if (repo_config)
-               free(repo_config);
+       free(repo_config);
        return ret;
 }
 
@@ -734,8 +733,7 @@ int git_config_set_multivar(const char* key, const char* value,
 out_free:
        if (0 <= fd)
                close(fd);
-       if (config_filename)
-               free(config_filename);
+       free(config_filename);
        if (lock_file) {
                unlink(lock_file);
                free(lock_file);
index 012936f3bd76d9f9648ba9498b4126c7d6861e5c..66ec830b7cab775e16c3fe06539e698edd6b7aff 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -7,6 +7,8 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
 #include "pkt-line.h"
 #include "cache.h"
 #include "exec_cmd.h"
@@ -19,7 +21,8 @@ static const char daemon_usage[] =
 "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
 "           [--base-path=path] [--user-path | --user-path=path]\n"
-"           [--reuseaddr] [--detach] [--pid-file=file] [directory...]";
+"           [--reuseaddr] [--detach] [--pid-file=file]\n"
+"           [--user=user [[--group=group]] [directory...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths;
@@ -526,7 +529,6 @@ static int socksetup(int port, int **socklist_p)
 
        for (ai = ai0; ai; ai = ai->ai_next) {
                int sockfd;
-               int *newlist;
 
                sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
                if (sockfd < 0)
@@ -560,11 +562,7 @@ static int socksetup(int port, int **socklist_p)
                        continue;       /* not fatal */
                }
 
-               newlist = realloc(socklist, sizeof(int) * (socknum + 1));
-               if (!newlist)
-                       die("memory allocation failed: %s", strerror(errno));
-
-               socklist = newlist;
+               socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
                socklist[socknum++] = sockfd;
 
                if (maxfd < sockfd)
@@ -701,7 +699,7 @@ static void store_pid(const char *path)
        fclose(f);
 }
 
-static int serve(int port)
+static int serve(int port, struct passwd *pass, gid_t gid)
 {
        int socknum, *socklist;
 
@@ -709,6 +707,11 @@ static int serve(int port)
        if (socknum == 0)
                die("unable to allocate any listen sockets on port %u", port);
 
+       if (pass && gid &&
+           (initgroups(pass->pw_name, gid) || setgid (gid) ||
+            setuid(pass->pw_uid)))
+               die("cannot drop privileges");
+
        return service_loop(socknum, socklist);
 }
 
@@ -716,8 +719,11 @@ int main(int argc, char **argv)
 {
        int port = DEFAULT_GIT_PORT;
        int inetd_mode = 0;
-       const char *pid_file = NULL;
+       const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
        int detach = 0;
+       struct passwd *pass = NULL;
+       struct group *group;
+       gid_t gid = 0;
        int i;
 
        /* Without this we cannot rely on waitpid() to tell
@@ -791,6 +797,14 @@ int main(int argc, char **argv)
                        log_syslog = 1;
                        continue;
                }
+               if (!strncmp(arg, "--user=", 7)) {
+                       user_name = arg + 7;
+                       continue;
+               }
+               if (!strncmp(arg, "--group=", 8)) {
+                       group_name = arg + 8;
+                       continue;
+               }
                if (!strcmp(arg, "--")) {
                        ok_paths = &argv[i+1];
                        break;
@@ -802,6 +816,28 @@ int main(int argc, char **argv)
                usage(daemon_usage);
        }
 
+       if (inetd_mode && (group_name || user_name))
+               die("--user and --group are incompatible with --inetd");
+
+       if (group_name && !user_name)
+               die("--group supplied without --user");
+
+       if (user_name) {
+               pass = getpwnam(user_name);
+               if (!pass)
+                       die("user not found - %s", user_name);
+
+               if (!group_name)
+                       gid = pass->pw_gid;
+               else {
+                       group = getgrnam(group_name);
+                       if (!group)
+                               die("group not found - %s", group_name);
+
+                       gid = group->gr_gid;
+               }
+       }
+
        if (log_syslog) {
                openlog("git-daemon", 0, LOG_DAEMON);
                set_die_routine(daemon_die);
@@ -831,5 +867,5 @@ int main(int argc, char **argv)
        if (pid_file)
                store_pid(pid_file);
 
-       return serve(port);
+       return serve(port, pass, gid);
 }
diff --git a/date.c b/date.c
index d780846b664530111a7bb67229987f202825e66d..e387dcd3976d8e12afdce5d7600ca0022f4ca835 100644 (file)
--- a/date.c
+++ b/date.c
@@ -37,6 +37,16 @@ static const char *weekday_names[] = {
        "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
 };
 
+static time_t gm_time_t(unsigned long time, int tz)
+{
+       int minutes;
+
+       minutes = tz < 0 ? -tz : tz;
+       minutes = (minutes / 100)*60 + (minutes % 100);
+       minutes = tz < 0 ? -minutes : minutes;
+       return time + minutes * 60;
+}
+
 /*
  * The "tz" thing is passed in as this strange "decimal parse of tz"
  * thing, which means that tz -0100 is passed in as the integer -100,
@@ -44,21 +54,58 @@ static const char *weekday_names[] = {
  */
 static struct tm *time_to_tm(unsigned long time, int tz)
 {
-       time_t t;
-       int minutes;
-
-       minutes = tz < 0 ? -tz : tz;
-       minutes = (minutes / 100)*60 + (minutes % 100);
-       minutes = tz < 0 ? -minutes : minutes;
-       t = time + minutes * 60;
+       time_t t = gm_time_t(time, tz);
        return gmtime(&t);
 }
 
-const char *show_date(unsigned long time, int tz)
+const char *show_date(unsigned long time, int tz, int relative)
 {
        struct tm *tm;
        static char timebuf[200];
 
+       if (relative) {
+               unsigned long diff;
+               time_t t = gm_time_t(time, tz);
+               struct timeval now;
+               gettimeofday(&now, NULL);
+               if (now.tv_sec < t)
+                       return "in the future";
+               diff = now.tv_sec - t;
+               if (diff < 90) {
+                       snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff);
+                       return timebuf;
+               }
+               /* Turn it into minutes */
+               diff = (diff + 30) / 60;
+               if (diff < 90) {
+                       snprintf(timebuf, sizeof(timebuf), "%lu minutes ago", diff);
+                       return timebuf;
+               }
+               /* Turn it into hours */
+               diff = (diff + 30) / 60;
+               if (diff < 36) {
+                       snprintf(timebuf, sizeof(timebuf), "%lu hours ago", diff);
+                       return timebuf;
+               }
+               /* We deal with number of days from here on */
+               diff = (diff + 12) / 24;
+               if (diff < 14) {
+                       snprintf(timebuf, sizeof(timebuf), "%lu days ago", diff);
+                       return timebuf;
+               }
+               /* Say weeks for the past 10 weeks or so */
+               if (diff < 70) {
+                       snprintf(timebuf, sizeof(timebuf), "%lu weeks ago", (diff + 3) / 7);
+                       return timebuf;
+               }
+               /* Say months for the past 12 months or so */
+               if (diff < 360) {
+                       snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
+                       return timebuf;
+               }
+               /* Else fall back on absolute format.. */
+       }
+
        tm = time_to_tm(time, tz);
        if (!tm)
                return NULL;
index 51e2e561772f490eb59f534a62754993bba2a743..fa16d06c8d1e85a458428c673cb2f589857f5424 100644 (file)
@@ -392,7 +392,7 @@ create_delta(const struct delta_index *index,
                                outsize = max_size + MAX_OP_SIZE + 1;
                        if (max_size && outpos > max_size)
                                break;
-                       out = realloc(out, outsize);
+                       out = xrealloc(out, outsize);
                        if (!out) {
                                free(tmp);
                                return NULL;
diff --git a/diff.c b/diff.c
index ca171e8e69680f8f873529729b129858a8201e08..70699fd8c48754215934a8ed45db336b9db68918 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -838,7 +838,7 @@ static unsigned char *deflate_it(char *data,
        return deflated;
 }
 
-static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+static void emit_binary_diff_body(mmfile_t *one, mmfile_t *two)
 {
        void *cp;
        void *delta;
@@ -849,7 +849,6 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
        unsigned long deflate_size;
        unsigned long data_size;
 
-       printf("GIT binary patch\n");
        /* We could do deflated delta, or we could do just deflated two,
         * whichever is smaller.
         */
@@ -898,6 +897,13 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
        free(data);
 }
 
+static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+{
+       printf("GIT binary patch\n");
+       emit_binary_diff_body(one, two);
+       emit_binary_diff_body(two, one);
+}
+
 #define FIRST_FEW_BYTES 8000
 static int mmfile_is_binary(mmfile_t *mf)
 {
diff --git a/dir.c b/dir.c
index 092d07736c60d4c6d86201ebeb841498c3be92a0..5a40d8ff8c99c80bf8f0484425db6022c6f7d8e9 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -101,8 +101,8 @@ void add_exclude(const char *string, const char *base,
        x->baselen = baselen;
        if (which->nr == which->alloc) {
                which->alloc = alloc_nr(which->alloc);
-               which->excludes = realloc(which->excludes,
-                                         which->alloc * sizeof(x));
+               which->excludes = xrealloc(which->excludes,
+                                          which->alloc * sizeof(x));
        }
        which->excludes[which->nr++] = x;
 }
@@ -112,17 +112,15 @@ static int add_excludes_from_file_1(const char *fname,
                                    int baselen,
                                    struct exclude_list *which)
 {
+       struct stat st;
        int fd, i;
        long size;
        char *buf, *entry;
 
        fd = open(fname, O_RDONLY);
-       if (fd < 0)
+       if (fd < 0 || fstat(fd, &st) < 0)
                goto err;
-       size = lseek(fd, 0, SEEK_END);
-       if (size < 0)
-               goto err;
-       lseek(fd, 0, SEEK_SET);
+       size = st.st_size;
        if (size == 0) {
                close(fd);
                return 0;
@@ -293,7 +291,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
        if (fdir) {
                int exclude_stk;
                struct dirent *de;
-               char fullname[MAXPATHLEN + 1];
+               char fullname[PATH_MAX + 1];
                memcpy(fullname, base, baselen);
 
                exclude_stk = push_exclude_per_directory(dir, base, baselen);
diff --git a/entry.c b/entry.c
index 793724fd52063157545f523f6d88953b96385c2c..b2ea0efa82e1a0511fe5aa798618c23827b59bab 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -135,7 +135,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
 
 int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath)
 {
-       static char path[MAXPATHLEN+1];
+       static char path[PATH_MAX + 1];
        struct stat st;
        int len = state->base_dir_len;
 
@@ -172,5 +172,3 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath)
        create_directories(path, state);
        return write_entry(ce, path, state, 0);
 }
-
-
diff --git a/fetch.c b/fetch.c
index ef60b045eaa0a55ba65499187a3aac082ffb6af2..7d3812c40606a6afad796826dd0937a92f71bc7a 100644 (file)
--- a/fetch.c
+++ b/fetch.c
@@ -302,8 +302,7 @@ int pull(int targets, char **target, const char **write_ref,
                if (ret)
                        goto unlock_and_fail;
        }
-       if (msg)
-               free(msg);
+       free(msg);
 
        return 0;
 
index f0e8831fa49ae46eb7ad664b6ad489e3974c9f91..8832573fee9d3b8d4be92786ae5116ee6e5405bb 100755 (executable)
@@ -51,9 +51,6 @@ patch=$tmp-patch
 mkdir $patch
 trap "rm -rf $tmp-*" 0 1 2 3 15
 
-_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"
-
 for c in $inup
 do
        git-diff-tree -p $c
index b2e18954c03ff502053cb74d142faab7d2a8dacb..91f2b0d3f0cf9d8539e7e09adc057dc337caae21 100644 (file)
@@ -172,7 +172,4 @@ static inline int sane_case(int x, int high)
        return x;
 }
 
-#ifndef MAXPATHLEN
-#define MAXPATHLEN 256
-#endif
 #endif
index a9cfafb1dfba6e85834a33caf6b3c69910594bf6..d049e164318a416941334b15699d52f87b00387f 100755 (executable)
@@ -9,15 +9,20 @@ USAGE='[-n] [--no-commit] [--squash] [-s <strategy>]... <merge-message> <head> <
 LF='
 '
 
-all_strategies='recursive octopus resolve stupid ours'
-default_twohead_strategies='recursive'
+all_strategies='recursive recur octopus resolve stupid ours'
+case "${GIT_USE_RECUR_FOR_RECURSIVE}" in
+'')
+       default_twohead_strategies=recursive ;;
+?*)
+       default_twohead_strategies=recur ;;
+esac
 default_octopus_strategies='octopus'
 no_trivial_merge_strategies='ours'
 use_strategies=
 
 index_merge=t
 if test "@@NO_PYTHON@@"; then
-       all_strategies='resolve octopus stupid ours'
+       all_strategies='recur resolve octopus stupid ours'
        default_twohead_strategies='resolve'
 fi
 
@@ -117,6 +122,10 @@ do
                        strategy="$2"
                        shift ;;
                esac
+               case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
+               recursive,?*)
+                       strategy=recur ;;
+               esac
                case " $all_strategies " in
                *" $strategy "*)
                        use_strategies="$use_strategies$strategy " ;;
index 7d3a5d0e718eebe6fb5d1fa3362f2a7087d6e691..20f74d416732e681d3b44ea610e428529af49235 100755 (executable)
@@ -35,7 +35,13 @@ If you would prefer to skip this patch, instead run \"git rebase --skip\".
 To restore the original branch and stop rebasing run \"git rebase --abort\".
 "
 unset newbase
-strategy=recursive
+case "${GIT_USE_RECUR_FOR_RECURSIVE}" in
+'')
+       strategy=recursive ;;
+?*)
+       strategy=recur ;;
+esac
+
 do_merge=
 dotest=$GIT_DIR/.dotest-merge
 prec=4
@@ -200,6 +206,11 @@ do
        shift
 done
 
+case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in
+recursive,?*)
+       strategy=recur ;;
+esac
+
 # Make sure we do not have .dotest
 if test -z "$do_merge"
 then
index 36fc8ce25b39682ba256a8d39bf9a393907ef43d..3133b5bd25ca7ad3c2a5451ee9c2e0f4514cd980 100755 (executable)
@@ -3,9 +3,6 @@
 USAGE='[--mixed | --soft | --hard]  [<commit-ish>]'
 . git-sh-setup
 
-tmp=${GIT_DIR}/reset.$$
-trap 'rm -f $tmp-*' 0 1 2 3 15
-
 update=
 reset_type=--mixed
 case "$1" in
index 9382a15044987fd97bfc659b2b0ed6eba334af1a..0290850b6639b4865d0941089d00a8c2ac5de366 100755 (executable)
@@ -819,6 +819,7 @@ sub commit_diff {
        } else {
                $ed->close_edit;
        }
+       $_message = $_file = undef;
 }
 
 ########################### utility functions #########################
diff --git a/git.c b/git.c
index a01d195c2319fd21fb361e535c5d6333550e3dbd..bd07289d71a75ad0280bae21c64c4c4be86f80d2 100644 (file)
--- a/git.c
+++ b/git.c
@@ -120,7 +120,7 @@ static int split_cmdline(char *cmdline, const char ***argv)
                                ; /* skip */
                        if (count >= size) {
                                size += 16;
-                               *argv = realloc(*argv, sizeof(char*) * size);
+                               *argv = xrealloc(*argv, sizeof(char*) * size);
                        }
                        (*argv)[count++] = cmdline + dst;
                } else if(!quoted && (c == '\'' || c == '"')) {
@@ -191,8 +191,8 @@ static int handle_alias(int *argcp, const char ***argv)
                        fflush(stderr);
                }
 
-               new_argv = realloc(new_argv, sizeof(char*) *
-                                  (count + *argcp + 1));
+               new_argv = xrealloc(new_argv, sizeof(char*) *
+                                   (count + *argcp + 1));
                /* insert after command name */
                memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
                new_argv[count+*argcp] = NULL;
@@ -263,6 +263,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
                { "tar-tree", cmd_tar_tree, RUN_SETUP },
+               { "zip-tree", cmd_zip_tree, RUN_SETUP },
                { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
                { "update-index", cmd_update_index, RUN_SETUP },
                { "update-ref", cmd_update_ref, RUN_SETUP },
index 7619b338feb92a431268fc5a3963415af1b53e99..6806f3678c2878d5d2550750394a998b2c5d54db 100644 (file)
@@ -696,10 +696,8 @@ xml_start_tag(void *userData, const char *name, const char **atts)
        strcat(ctx->name, ".");
        strcat(ctx->name, c);
 
-       if (ctx->cdata) {
-               free(ctx->cdata);
-               ctx->cdata = NULL;
-       }
+       free(ctx->cdata);
+       ctx->cdata = NULL;
 
        ctx->userFunc(ctx, 0);
 }
@@ -726,8 +724,7 @@ static void
 xml_cdata(void *userData, const XML_Char *s, int len)
 {
        struct xml_ctx *ctx = (struct xml_ctx *)userData;
-       if (ctx->cdata)
-               free(ctx->cdata);
+       free(ctx->cdata);
        ctx->cdata = xmalloc(len + 1);
        strlcpy(ctx->cdata, s, len + 1);
 }
@@ -765,9 +762,7 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
                        ls->dentry_flags |= IS_DIR;
                }
        } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
-               if (ls->dentry_name) {
-                       free(ls->dentry_name);
-               }
+               free(ls->dentry_name);
                ls->dentry_name = NULL;
                ls->dentry_flags = 0;
        }
index 04cb238e965efdff564a9b57ce1253b274268c46..7814666d8df2e202c547cf05f0768bdc3830873c 100644 (file)
@@ -1238,10 +1238,8 @@ xml_start_tag(void *userData, const char *name, const char **atts)
        strcat(ctx->name, ".");
        strcat(ctx->name, c);
 
-       if (ctx->cdata) {
-               free(ctx->cdata);
-               ctx->cdata = NULL;
-       }
+       free(ctx->cdata);
+       ctx->cdata = NULL;
 
        ctx->userFunc(ctx, 0);
 }
@@ -1268,8 +1266,7 @@ static void
 xml_cdata(void *userData, const XML_Char *s, int len)
 {
        struct xml_ctx *ctx = (struct xml_ctx *)userData;
-       if (ctx->cdata)
-               free(ctx->cdata);
+       free(ctx->cdata);
        ctx->cdata = xmalloc(len + 1);
        strlcpy(ctx->cdata, s, len + 1);
 }
@@ -1518,9 +1515,7 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
                        ls->dentry_flags |= IS_DIR;
                }
        } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
-               if (ls->dentry_name) {
-                       free(ls->dentry_name);
-               }
+               free(ls->dentry_name);
                ls->dentry_name = NULL;
                ls->dentry_flags = 0;
        }
diff --git a/merge-recursive.c b/merge-recursive.c
new file mode 100644 (file)
index 0000000..39a1eae
--- /dev/null
@@ -0,0 +1,1354 @@
+/*
+ * Recursive Merge algorithm stolen from git-merge-recursive.py by
+ * Fredrik Kuivinen.
+ * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "cache.h"
+#include "cache-tree.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "run-command.h"
+#include "tag.h"
+#include "unpack-trees.h"
+#include "path-list.h"
+
+/*
+ * A virtual commit has
+ * - (const char *)commit->util set to the name, and
+ * - *(int *)commit->object.sha1 set to the virtual id.
+ */
+
+static unsigned commit_list_count(const struct commit_list *l)
+{
+       unsigned c = 0;
+       for (; l; l = l->next )
+               c++;
+       return c;
+}
+
+static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
+{
+       struct commit *commit = xcalloc(1, sizeof(struct commit));
+       static unsigned virtual_id = 1;
+       commit->tree = tree;
+       commit->util = (void*)comment;
+       *(int*)commit->object.sha1 = virtual_id++;
+       /* avoid warnings */
+       commit->object.parsed = 1;
+       return commit;
+}
+
+/*
+ * Since we use get_tree_entry(), which does not put the read object into
+ * the object pool, we cannot rely on a == b.
+ */
+static int sha_eq(const unsigned char *a, const unsigned char *b)
+{
+       if (!a && !b)
+               return 2;
+       return a && b && hashcmp(a, b) == 0;
+}
+
+/*
+ * Since we want to write the index eventually, we cannot reuse the index
+ * for these (temporary) data.
+ */
+struct stage_data
+{
+       struct
+       {
+               unsigned mode;
+               unsigned char sha[20];
+       } stages[4];
+       unsigned processed:1;
+};
+
+static struct path_list current_file_set = {NULL, 0, 0, 1};
+static struct path_list current_directory_set = {NULL, 0, 0, 1};
+
+static int output_indent = 0;
+
+static void output(const char *fmt, ...)
+{
+       va_list args;
+       int i;
+       for (i = output_indent; i--;)
+               fputs("  ", stdout);
+       va_start(args, fmt);
+       vfprintf(stdout, fmt, args);
+       va_end(args);
+       fputc('\n', stdout);
+}
+
+static void output_commit_title(struct commit *commit)
+{
+       int i;
+       for (i = output_indent; i--;)
+               fputs("  ", stdout);
+       if (commit->util)
+               printf("virtual %s\n", (char *)commit->util);
+       else {
+               printf("%s ", sha1_to_hex(commit->object.sha1));
+               if (parse_commit(commit) != 0)
+                       printf("(bad commit)\n");
+               else {
+                       const char *s;
+                       int len;
+                       for (s = commit->buffer; *s; s++)
+                               if (*s == '\n' && s[1] == '\n') {
+                                       s += 2;
+                                       break;
+                               }
+                       for (len = 0; s[len] && '\n' != s[len]; len++)
+                               ; /* do nothing */
+                       printf("%.*s\n", len, s);
+               }
+       }
+}
+
+static const char *current_index_file = NULL;
+static const char *original_index_file;
+static const char *temporary_index_file;
+static int cache_dirty = 0;
+
+static int flush_cache(void)
+{
+       /* flush temporary index */
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       int fd = hold_lock_file_for_update(lock, current_index_file, 1);
+       if (write_cache(fd, active_cache, active_nr) ||
+                       close(fd) || commit_lock_file(lock))
+               die ("unable to write %s", current_index_file);
+       discard_cache();
+       cache_dirty = 0;
+       return 0;
+}
+
+static void setup_index(int temp)
+{
+       current_index_file = temp ? temporary_index_file: original_index_file;
+       if (cache_dirty) {
+               discard_cache();
+               cache_dirty = 0;
+       }
+       unlink(temporary_index_file);
+       discard_cache();
+}
+
+static struct cache_entry *make_cache_entry(unsigned int mode,
+               const unsigned char *sha1, const char *path, int stage, int refresh)
+{
+       int size, len;
+       struct cache_entry *ce;
+
+       if (!verify_path(path))
+               return NULL;
+
+       len = strlen(path);
+       size = cache_entry_size(len);
+       ce = xcalloc(1, size);
+
+       hashcpy(ce->sha1, sha1);
+       memcpy(ce->name, path, len);
+       ce->ce_flags = create_ce_flags(len, stage);
+       ce->ce_mode = create_ce_mode(mode);
+
+       if (refresh)
+               return refresh_cache_entry(ce, 0);
+
+       return ce;
+}
+
+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
+               const char *path, int stage, int refresh, int options)
+{
+       struct cache_entry *ce;
+       if (!cache_dirty)
+               read_cache_from(current_index_file);
+       cache_dirty++;
+       ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
+       if (!ce)
+               return error("cache_addinfo failed: %s", strerror(cache_errno));
+       return add_cache_entry(ce, options);
+}
+
+/*
+ * This is a global variable which is used in a number of places but
+ * only written to in the 'merge' function.
+ *
+ * index_only == 1    => Don't leave any non-stage 0 entries in the cache and
+ *                       don't update the working directory.
+ *               0    => Leave unmerged entries in the cache and update
+ *                       the working directory.
+ */
+static int index_only = 0;
+
+static int git_read_tree(struct tree *tree)
+{
+       int rc;
+       struct object_list *trees = NULL;
+       struct unpack_trees_options opts;
+
+       if (cache_dirty)
+               die("read-tree with dirty cache");
+
+       memset(&opts, 0, sizeof(opts));
+       object_list_append(&tree->object, &trees);
+       rc = unpack_trees(trees, &opts);
+       cache_tree_free(&active_cache_tree);
+
+       if (rc == 0)
+               cache_dirty = 1;
+
+       return rc;
+}
+
+static int git_merge_trees(int index_only,
+                          struct tree *common,
+                          struct tree *head,
+                          struct tree *merge)
+{
+       int rc;
+       struct object_list *trees = NULL;
+       struct unpack_trees_options opts;
+
+       if (!cache_dirty) {
+               read_cache_from(current_index_file);
+               cache_dirty = 1;
+       }
+
+       memset(&opts, 0, sizeof(opts));
+       if (index_only)
+               opts.index_only = 1;
+       else
+               opts.update = 1;
+       opts.merge = 1;
+       opts.head_idx = 2;
+       opts.fn = threeway_merge;
+
+       object_list_append(&common->object, &trees);
+       object_list_append(&head->object, &trees);
+       object_list_append(&merge->object, &trees);
+
+       rc = unpack_trees(trees, &opts);
+       cache_tree_free(&active_cache_tree);
+
+       cache_dirty = 1;
+
+       return rc;
+}
+
+static struct tree *git_write_tree(void)
+{
+       struct tree *result = NULL;
+
+       if (cache_dirty) {
+               unsigned i;
+               for (i = 0; i < active_nr; i++) {
+                       struct cache_entry *ce = active_cache[i];
+                       if (ce_stage(ce))
+                               return NULL;
+               }
+       } else
+               read_cache_from(current_index_file);
+
+       if (!active_cache_tree)
+               active_cache_tree = cache_tree();
+
+       if (!cache_tree_fully_valid(active_cache_tree) &&
+                       cache_tree_update(active_cache_tree,
+                               active_cache, active_nr, 0, 0) < 0)
+               die("error building trees");
+
+       result = lookup_tree(active_cache_tree->sha1);
+
+       flush_cache();
+       cache_dirty = 0;
+
+       return result;
+}
+
+static int save_files_dirs(const unsigned char *sha1,
+               const char *base, int baselen, const char *path,
+               unsigned int mode, int stage)
+{
+       int len = strlen(path);
+       char *newpath = malloc(baselen + len + 1);
+       memcpy(newpath, base, baselen);
+       memcpy(newpath + baselen, path, len);
+       newpath[baselen + len] = '\0';
+
+       if (S_ISDIR(mode))
+               path_list_insert(newpath, &current_directory_set);
+       else
+               path_list_insert(newpath, &current_file_set);
+       free(newpath);
+
+       return READ_TREE_RECURSIVE;
+}
+
+static int get_files_dirs(struct tree *tree)
+{
+       int n;
+       if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0)
+               return 0;
+       n = current_file_set.nr + current_directory_set.nr;
+       return n;
+}
+
+/*
+ * Returns a index_entry instance which doesn't have to correspond to
+ * a real cache entry in Git's index.
+ */
+static struct stage_data *insert_stage_data(const char *path,
+               struct tree *o, struct tree *a, struct tree *b,
+               struct path_list *entries)
+{
+       struct path_list_item *item;
+       struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
+       get_tree_entry(o->object.sha1, path,
+                       e->stages[1].sha, &e->stages[1].mode);
+       get_tree_entry(a->object.sha1, path,
+                       e->stages[2].sha, &e->stages[2].mode);
+       get_tree_entry(b->object.sha1, path,
+                       e->stages[3].sha, &e->stages[3].mode);
+       item = path_list_insert(path, entries);
+       item->util = e;
+       return e;
+}
+
+/*
+ * Create a dictionary mapping file names to stage_data objects. The
+ * dictionary contains one entry for every path with a non-zero stage entry.
+ */
+static struct path_list *get_unmerged(void)
+{
+       struct path_list *unmerged = xcalloc(1, sizeof(struct path_list));
+       int i;
+
+       unmerged->strdup_paths = 1;
+       if (!cache_dirty) {
+               read_cache_from(current_index_file);
+               cache_dirty++;
+       }
+       for (i = 0; i < active_nr; i++) {
+               struct path_list_item *item;
+               struct stage_data *e;
+               struct cache_entry *ce = active_cache[i];
+               if (!ce_stage(ce))
+                       continue;
+
+               item = path_list_lookup(ce->name, unmerged);
+               if (!item) {
+                       item = path_list_insert(ce->name, unmerged);
+                       item->util = xcalloc(1, sizeof(struct stage_data));
+               }
+               e = item->util;
+               e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);
+               hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1);
+       }
+
+       return unmerged;
+}
+
+struct rename
+{
+       struct diff_filepair *pair;
+       struct stage_data *src_entry;
+       struct stage_data *dst_entry;
+       unsigned processed:1;
+};
+
+/*
+ * Get information of all renames which occured between 'o_tree' and
+ * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and
+ * 'b_tree') to be able to associate the correct cache entries with
+ * the rename information. 'tree' is always equal to either a_tree or b_tree.
+ */
+static struct path_list *get_renames(struct tree *tree,
+                                       struct tree *o_tree,
+                                       struct tree *a_tree,
+                                       struct tree *b_tree,
+                                       struct path_list *entries)
+{
+       int i;
+       struct path_list *renames;
+       struct diff_options opts;
+
+       renames = xcalloc(1, sizeof(struct path_list));
+       diff_setup(&opts);
+       opts.recursive = 1;
+       opts.detect_rename = DIFF_DETECT_RENAME;
+       opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+       if (diff_setup_done(&opts) < 0)
+               die("diff setup failed");
+       diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
+       diffcore_std(&opts);
+       for (i = 0; i < diff_queued_diff.nr; ++i) {
+               struct path_list_item *item;
+               struct rename *re;
+               struct diff_filepair *pair = diff_queued_diff.queue[i];
+               if (pair->status != 'R') {
+                       diff_free_filepair(pair);
+                       continue;
+               }
+               re = xmalloc(sizeof(*re));
+               re->processed = 0;
+               re->pair = pair;
+               item = path_list_lookup(re->pair->one->path, entries);
+               if (!item)
+                       re->src_entry = insert_stage_data(re->pair->one->path,
+                                       o_tree, a_tree, b_tree, entries);
+               else
+                       re->src_entry = item->util;
+
+               item = path_list_lookup(re->pair->two->path, entries);
+               if (!item)
+                       re->dst_entry = insert_stage_data(re->pair->two->path,
+                                       o_tree, a_tree, b_tree, entries);
+               else
+                       re->dst_entry = item->util;
+               item = path_list_insert(pair->one->path, renames);
+               item->util = re;
+       }
+       opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+       diff_queued_diff.nr = 0;
+       diff_flush(&opts);
+       return renames;
+}
+
+int update_stages(const char *path, struct diff_filespec *o,
+               struct diff_filespec *a, struct diff_filespec *b, int clear)
+{
+       int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+       if (clear)
+               if (remove_file_from_cache(path))
+                       return -1;
+       if (o)
+               if (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))
+                       return -1;
+       if (a)
+               if (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))
+                       return -1;
+       if (b)
+               if (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))
+                       return -1;
+       return 0;
+}
+
+static int remove_path(const char *name)
+{
+       int ret, len;
+       char *slash, *dirs;
+
+       ret = unlink(name);
+       if (ret)
+               return ret;
+       len = strlen(name);
+       dirs = malloc(len+1);
+       memcpy(dirs, name, len);
+       dirs[len] = '\0';
+       while ((slash = strrchr(name, '/'))) {
+               *slash = '\0';
+               len = slash - name;
+               if (rmdir(name) != 0)
+                       break;
+       }
+       free(dirs);
+       return ret;
+}
+
+int remove_file(int clean, const char *path)
+{
+       int update_cache = index_only || clean;
+       int update_working_directory = !index_only;
+
+       if (update_cache) {
+               if (!cache_dirty)
+                       read_cache_from(current_index_file);
+               cache_dirty++;
+               if (remove_file_from_cache(path))
+                       return -1;
+       }
+       if (update_working_directory)
+       {
+               unlink(path);
+               if (errno != ENOENT || errno != EISDIR)
+                       return -1;
+               remove_path(path);
+       }
+       return 0;
+}
+
+static char *unique_path(const char *path, const char *branch)
+{
+       char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+       int suffix = 0;
+       struct stat st;
+       char *p = newpath + strlen(path);
+       strcpy(newpath, path);
+       *(p++) = '~';
+       strcpy(p, branch);
+       for (; *p; ++p)
+               if ('/' == *p)
+                       *p = '_';
+       while (path_list_has_path(&current_file_set, newpath) ||
+              path_list_has_path(&current_directory_set, newpath) ||
+              lstat(newpath, &st) == 0)
+               sprintf(p, "_%d", suffix++);
+
+       path_list_insert(newpath, &current_file_set);
+       return newpath;
+}
+
+static int mkdir_p(const char *path, unsigned long mode)
+{
+       /* path points to cache entries, so strdup before messing with it */
+       char *buf = strdup(path);
+       int result = safe_create_leading_directories(buf);
+       free(buf);
+       return result;
+}
+
+static void flush_buffer(int fd, const char *buf, unsigned long size)
+{
+       while (size > 0) {
+               long ret = xwrite(fd, buf, size);
+               if (ret < 0) {
+                       /* Ignore epipe */
+                       if (errno == EPIPE)
+                               break;
+                       die("merge-recursive: %s", strerror(errno));
+               } else if (!ret) {
+                       die("merge-recursive: disk full?");
+               }
+               size -= ret;
+               buf += ret;
+       }
+}
+
+void update_file_flags(const unsigned char *sha,
+                      unsigned mode,
+                      const char *path,
+                      int update_cache,
+                      int update_wd)
+{
+       if (index_only)
+               update_wd = 0;
+
+       if (update_wd) {
+               char type[20];
+               void *buf;
+               unsigned long size;
+
+               buf = read_sha1_file(sha, type, &size);
+               if (!buf)
+                       die("cannot read object %s '%s'", sha1_to_hex(sha), path);
+               if (strcmp(type, blob_type) != 0)
+                       die("blob expected for %s '%s'", sha1_to_hex(sha), path);
+
+               if (S_ISREG(mode)) {
+                       int fd;
+                       if (mkdir_p(path, 0777))
+                               die("failed to create path %s: %s", path, strerror(errno));
+                       unlink(path);
+                       if (mode & 0100)
+                               mode = 0777;
+                       else
+                               mode = 0666;
+                       fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
+                       if (fd < 0)
+                               die("failed to open %s: %s", path, strerror(errno));
+                       flush_buffer(fd, buf, size);
+                       close(fd);
+               } else if (S_ISLNK(mode)) {
+                       char *lnk = malloc(size + 1);
+                       memcpy(lnk, buf, size);
+                       lnk[size] = '\0';
+                       mkdir_p(path, 0777);
+                       unlink(lnk);
+                       symlink(lnk, path);
+               } else
+                       die("do not know what to do with %06o %s '%s'",
+                           mode, sha1_to_hex(sha), path);
+       }
+       if (update_cache)
+               add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
+}
+
+void update_file(int clean,
+               const unsigned char *sha,
+               unsigned mode,
+               const char *path)
+{
+       update_file_flags(sha, mode, path, index_only || clean, !index_only);
+}
+
+/* Low level file merging, update and removal */
+
+struct merge_file_info
+{
+       unsigned char sha[20];
+       unsigned mode;
+       unsigned clean:1,
+                merge:1;
+};
+
+static char *git_unpack_file(const unsigned char *sha1, char *path)
+{
+       void *buf;
+       char type[20];
+       unsigned long size;
+       int fd;
+
+       buf = read_sha1_file(sha1, type, &size);
+       if (!buf || strcmp(type, blob_type))
+               die("unable to read blob object %s", sha1_to_hex(sha1));
+
+       strcpy(path, ".merge_file_XXXXXX");
+       fd = mkstemp(path);
+       if (fd < 0)
+               die("unable to create temp-file");
+       flush_buffer(fd, buf, size);
+       close(fd);
+       return path;
+}
+
+static struct merge_file_info merge_file(struct diff_filespec *o,
+               struct diff_filespec *a, struct diff_filespec *b,
+               const char *branch1, const char *branch2)
+{
+       struct merge_file_info result;
+       result.merge = 0;
+       result.clean = 1;
+
+       if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
+               result.clean = 0;
+               if (S_ISREG(a->mode)) {
+                       result.mode = a->mode;
+                       hashcpy(result.sha, a->sha1);
+               } else {
+                       result.mode = b->mode;
+                       hashcpy(result.sha, b->sha1);
+               }
+       } else {
+               if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
+                       result.merge = 1;
+
+               result.mode = a->mode == o->mode ? b->mode: a->mode;
+
+               if (sha_eq(a->sha1, o->sha1))
+                       hashcpy(result.sha, b->sha1);
+               else if (sha_eq(b->sha1, o->sha1))
+                       hashcpy(result.sha, a->sha1);
+               else if (S_ISREG(a->mode)) {
+                       int code = 1, fd;
+                       struct stat st;
+                       char orig[PATH_MAX];
+                       char src1[PATH_MAX];
+                       char src2[PATH_MAX];
+                       const char *argv[] = {
+                               "merge", "-L", NULL, "-L", NULL, "-L", NULL,
+                               NULL, NULL, NULL,
+                               NULL
+                       };
+                       char *la, *lb, *lo;
+
+                       git_unpack_file(o->sha1, orig);
+                       git_unpack_file(a->sha1, src1);
+                       git_unpack_file(b->sha1, src2);
+
+                       argv[2] = la = strdup(mkpath("%s/%s", branch1, a->path));
+                       argv[6] = lb = strdup(mkpath("%s/%s", branch2, b->path));
+                       argv[4] = lo = strdup(mkpath("orig/%s", o->path));
+                       argv[7] = src1;
+                       argv[8] = orig;
+                       argv[9] = src2,
+
+                       code = run_command_v(10, argv);
+
+                       free(la);
+                       free(lb);
+                       free(lo);
+                       if (code && code < -256) {
+                               die("Failed to execute 'merge'. merge(1) is used as the "
+                                   "file-level merge tool. Is 'merge' in your path?");
+                       }
+                       fd = open(src1, O_RDONLY);
+                       if (fd < 0 || fstat(fd, &st) < 0 ||
+                                       index_fd(result.sha, fd, &st, 1,
+                                               "blob"))
+                               die("Unable to add %s to database", src1);
+
+                       unlink(orig);
+                       unlink(src1);
+                       unlink(src2);
+
+                       result.clean = WEXITSTATUS(code) == 0;
+               } else {
+                       if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode)))
+                               die("cannot merge modes?");
+
+                       hashcpy(result.sha, a->sha1);
+
+                       if (!sha_eq(a->sha1, b->sha1))
+                               result.clean = 0;
+               }
+       }
+
+       return result;
+}
+
+static void conflict_rename_rename(struct rename *ren1,
+                                  const char *branch1,
+                                  struct rename *ren2,
+                                  const char *branch2)
+{
+       char *del[2];
+       int delp = 0;
+       const char *ren1_dst = ren1->pair->two->path;
+       const char *ren2_dst = ren2->pair->two->path;
+       const char *dst_name1 = ren1_dst;
+       const char *dst_name2 = ren2_dst;
+       if (path_list_has_path(&current_directory_set, ren1_dst)) {
+               dst_name1 = del[delp++] = unique_path(ren1_dst, branch1);
+               output("%s is a directory in %s adding as %s instead",
+                      ren1_dst, branch2, dst_name1);
+               remove_file(0, ren1_dst);
+       }
+       if (path_list_has_path(&current_directory_set, ren2_dst)) {
+               dst_name2 = del[delp++] = unique_path(ren2_dst, branch2);
+               output("%s is a directory in %s adding as %s instead",
+                      ren2_dst, branch1, dst_name2);
+               remove_file(0, ren2_dst);
+       }
+       update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
+       update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
+       while (delp--)
+               free(del[delp]);
+}
+
+static void conflict_rename_dir(struct rename *ren1,
+                               const char *branch1)
+{
+       char *new_path = unique_path(ren1->pair->two->path, branch1);
+       output("Renaming %s to %s instead", ren1->pair->one->path, new_path);
+       remove_file(0, ren1->pair->two->path);
+       update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
+       free(new_path);
+}
+
+static void conflict_rename_rename_2(struct rename *ren1,
+                                    const char *branch1,
+                                    struct rename *ren2,
+                                    const char *branch2)
+{
+       char *new_path1 = unique_path(ren1->pair->two->path, branch1);
+       char *new_path2 = unique_path(ren2->pair->two->path, branch2);
+       output("Renaming %s to %s and %s to %s instead",
+              ren1->pair->one->path, new_path1,
+              ren2->pair->one->path, new_path2);
+       remove_file(0, ren1->pair->two->path);
+       update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1);
+       update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2);
+       free(new_path2);
+       free(new_path1);
+}
+
+static int process_renames(struct path_list *a_renames,
+                          struct path_list *b_renames,
+                          const char *a_branch,
+                          const char *b_branch)
+{
+       int clean_merge = 1, i, j;
+       struct path_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
+       const struct rename *sre;
+
+       for (i = 0; i < a_renames->nr; i++) {
+               sre = a_renames->items[i].util;
+               path_list_insert(sre->pair->two->path, &a_by_dst)->util
+                       = sre->dst_entry;
+       }
+       for (i = 0; i < b_renames->nr; i++) {
+               sre = b_renames->items[i].util;
+               path_list_insert(sre->pair->two->path, &b_by_dst)->util
+                       = sre->dst_entry;
+       }
+
+       for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
+               int compare;
+               char *src;
+               struct path_list *renames1, *renames2, *renames2Dst;
+               struct rename *ren1 = NULL, *ren2 = NULL;
+               const char *branch1, *branch2;
+               const char *ren1_src, *ren1_dst;
+
+               if (i >= a_renames->nr) {
+                       compare = 1;
+                       ren2 = b_renames->items[j++].util;
+               } else if (j >= b_renames->nr) {
+                       compare = -1;
+                       ren1 = a_renames->items[i++].util;
+               } else {
+                       compare = strcmp(a_renames->items[i].path,
+                                       b_renames->items[j].path);
+                       if (compare <= 0)
+                               ren1 = a_renames->items[i++].util;
+                       if (compare >= 0)
+                               ren2 = b_renames->items[j++].util;
+               }
+
+               /* TODO: refactor, so that 1/2 are not needed */
+               if (ren1) {
+                       renames1 = a_renames;
+                       renames2 = b_renames;
+                       renames2Dst = &b_by_dst;
+                       branch1 = a_branch;
+                       branch2 = b_branch;
+               } else {
+                       struct rename *tmp;
+                       renames1 = b_renames;
+                       renames2 = a_renames;
+                       renames2Dst = &a_by_dst;
+                       branch1 = b_branch;
+                       branch2 = a_branch;
+                       tmp = ren2;
+                       ren2 = ren1;
+                       ren1 = tmp;
+               }
+               src = ren1->pair->one->path;
+
+               ren1->dst_entry->processed = 1;
+               ren1->src_entry->processed = 1;
+
+               if (ren1->processed)
+                       continue;
+               ren1->processed = 1;
+
+               ren1_src = ren1->pair->one->path;
+               ren1_dst = ren1->pair->two->path;
+
+               if (ren2) {
+                       const char *ren2_src = ren2->pair->one->path;
+                       const char *ren2_dst = ren2->pair->two->path;
+                       /* Renamed in 1 and renamed in 2 */
+                       if (strcmp(ren1_src, ren2_src) != 0)
+                               die("ren1.src != ren2.src");
+                       ren2->dst_entry->processed = 1;
+                       ren2->processed = 1;
+                       if (strcmp(ren1_dst, ren2_dst) != 0) {
+                               clean_merge = 0;
+                               output("CONFLICT (rename/rename): "
+                                      "Rename %s->%s in branch %s "
+                                      "rename %s->%s in %s",
+                                      src, ren1_dst, branch1,
+                                      src, ren2_dst, branch2);
+                               conflict_rename_rename(ren1, branch1, ren2, branch2);
+                       } else {
+                               struct merge_file_info mfi;
+                               remove_file(1, ren1_src);
+                               mfi = merge_file(ren1->pair->one,
+                                                ren1->pair->two,
+                                                ren2->pair->two,
+                                                branch1,
+                                                branch2);
+                               if (mfi.merge || !mfi.clean)
+                                       output("Renaming %s->%s", src, ren1_dst);
+
+                               if (mfi.merge)
+                                       output("Auto-merging %s", ren1_dst);
+
+                               if (!mfi.clean) {
+                                       output("CONFLICT (content): merge conflict in %s",
+                                              ren1_dst);
+                                       clean_merge = 0;
+
+                                       if (!index_only)
+                                               update_stages(ren1_dst,
+                                                             ren1->pair->one,
+                                                             ren1->pair->two,
+                                                             ren2->pair->two,
+                                                             1 /* clear */);
+                               }
+                               update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+                       }
+               } else {
+                       /* Renamed in 1, maybe changed in 2 */
+                       struct path_list_item *item;
+                       /* we only use sha1 and mode of these */
+                       struct diff_filespec src_other, dst_other;
+                       int try_merge, stage = a_renames == renames1 ? 3: 2;
+
+                       remove_file(1, ren1_src);
+
+                       hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha);
+                       src_other.mode = ren1->src_entry->stages[stage].mode;
+                       hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha);
+                       dst_other.mode = ren1->dst_entry->stages[stage].mode;
+
+                       try_merge = 0;
+
+                       if (path_list_has_path(&current_directory_set, ren1_dst)) {
+                               clean_merge = 0;
+                               output("CONFLICT (rename/directory): Rename %s->%s in %s "
+                                      " directory %s added in %s",
+                                      ren1_src, ren1_dst, branch1,
+                                      ren1_dst, branch2);
+                               conflict_rename_dir(ren1, branch1);
+                       } else if (sha_eq(src_other.sha1, null_sha1)) {
+                               clean_merge = 0;
+                               output("CONFLICT (rename/delete): Rename %s->%s in %s "
+                                      "and deleted in %s",
+                                      ren1_src, ren1_dst, branch1,
+                                      branch2);
+                               update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
+                       } else if (!sha_eq(dst_other.sha1, null_sha1)) {
+                               const char *new_path;
+                               clean_merge = 0;
+                               try_merge = 1;
+                               output("CONFLICT (rename/add): Rename %s->%s in %s. "
+                                      "%s added in %s",
+                                      ren1_src, ren1_dst, branch1,
+                                      ren1_dst, branch2);
+                               new_path = unique_path(ren1_dst, branch2);
+                               output("Adding as %s instead", new_path);
+                               update_file(0, dst_other.sha1, dst_other.mode, new_path);
+                       } else if ((item = path_list_lookup(ren1_dst, renames2Dst))) {
+                               ren2 = item->util;
+                               clean_merge = 0;
+                               ren2->processed = 1;
+                               output("CONFLICT (rename/rename): Rename %s->%s in %s. "
+                                      "Rename %s->%s in %s",
+                                      ren1_src, ren1_dst, branch1,
+                                      ren2->pair->one->path, ren2->pair->two->path, branch2);
+                               conflict_rename_rename_2(ren1, branch1, ren2, branch2);
+                       } else
+                               try_merge = 1;
+
+                       if (try_merge) {
+                               struct diff_filespec *o, *a, *b;
+                               struct merge_file_info mfi;
+                               src_other.path = (char *)ren1_src;
+
+                               o = ren1->pair->one;
+                               if (a_renames == renames1) {
+                                       a = ren1->pair->two;
+                                       b = &src_other;
+                               } else {
+                                       b = ren1->pair->two;
+                                       a = &src_other;
+                               }
+                               mfi = merge_file(o, a, b,
+                                               a_branch, b_branch);
+
+                               if (mfi.merge || !mfi.clean)
+                                       output("Renaming %s => %s", ren1_src, ren1_dst);
+                               if (mfi.merge)
+                                       output("Auto-merging %s", ren1_dst);
+                               if (!mfi.clean) {
+                                       output("CONFLICT (rename/modify): Merge conflict in %s",
+                                              ren1_dst);
+                                       clean_merge = 0;
+
+                                       if (!index_only)
+                                               update_stages(ren1_dst,
+                                                               o, a, b, 1);
+                               }
+                               update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+                       }
+               }
+       }
+       path_list_clear(&a_by_dst, 0);
+       path_list_clear(&b_by_dst, 0);
+
+       if (cache_dirty)
+               flush_cache();
+       return clean_merge;
+}
+
+static unsigned char *has_sha(const unsigned char *sha)
+{
+       return is_null_sha1(sha) ? NULL: (unsigned char *)sha;
+}
+
+/* Per entry merge function */
+static int process_entry(const char *path, struct stage_data *entry,
+                        const char *branch1,
+                        const char *branch2)
+{
+       /*
+       printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
+       print_index_entry("\tpath: ", entry);
+       */
+       int clean_merge = 1;
+       unsigned char *o_sha = has_sha(entry->stages[1].sha);
+       unsigned char *a_sha = has_sha(entry->stages[2].sha);
+       unsigned char *b_sha = has_sha(entry->stages[3].sha);
+       unsigned o_mode = entry->stages[1].mode;
+       unsigned a_mode = entry->stages[2].mode;
+       unsigned b_mode = entry->stages[3].mode;
+
+       if (o_sha && (!a_sha || !b_sha)) {
+               /* Case A: Deleted in one */
+               if ((!a_sha && !b_sha) ||
+                   (sha_eq(a_sha, o_sha) && !b_sha) ||
+                   (!a_sha && sha_eq(b_sha, o_sha))) {
+                       /* Deleted in both or deleted in one and
+                        * unchanged in the other */
+                       if (a_sha)
+                               output("Removing %s", path);
+                       remove_file(1, path);
+               } else {
+                       /* Deleted in one and changed in the other */
+                       clean_merge = 0;
+                       if (!a_sha) {
+                               output("CONFLICT (delete/modify): %s deleted in %s "
+                                      "and modified in %s. Version %s of %s left in tree.",
+                                      path, branch1,
+                                      branch2, branch2, path);
+                               update_file(0, b_sha, b_mode, path);
+                       } else {
+                               output("CONFLICT (delete/modify): %s deleted in %s "
+                                      "and modified in %s. Version %s of %s left in tree.",
+                                      path, branch2,
+                                      branch1, branch1, path);
+                               update_file(0, a_sha, a_mode, path);
+                       }
+               }
+
+       } else if ((!o_sha && a_sha && !b_sha) ||
+                  (!o_sha && !a_sha && b_sha)) {
+               /* Case B: Added in one. */
+               const char *add_branch;
+               const char *other_branch;
+               unsigned mode;
+               const unsigned char *sha;
+               const char *conf;
+
+               if (a_sha) {
+                       add_branch = branch1;
+                       other_branch = branch2;
+                       mode = a_mode;
+                       sha = a_sha;
+                       conf = "file/directory";
+               } else {
+                       add_branch = branch2;
+                       other_branch = branch1;
+                       mode = b_mode;
+                       sha = b_sha;
+                       conf = "directory/file";
+               }
+               if (path_list_has_path(&current_directory_set, path)) {
+                       const char *new_path = unique_path(path, add_branch);
+                       clean_merge = 0;
+                       output("CONFLICT (%s): There is a directory with name %s in %s. "
+                              "Adding %s as %s",
+                              conf, path, other_branch, path, new_path);
+                       remove_file(0, path);
+                       update_file(0, sha, mode, new_path);
+               } else {
+                       output("Adding %s", path);
+                       update_file(1, sha, mode, path);
+               }
+       } else if (!o_sha && a_sha && b_sha) {
+               /* Case C: Added in both (check for same permissions). */
+               if (sha_eq(a_sha, b_sha)) {
+                       if (a_mode != b_mode) {
+                               clean_merge = 0;
+                               output("CONFLICT: File %s added identically in both branches, "
+                                      "but permissions conflict %06o->%06o",
+                                      path, a_mode, b_mode);
+                               output("CONFLICT: adding with permission: %06o", a_mode);
+                               update_file(0, a_sha, a_mode, path);
+                       } else {
+                               /* This case is handled by git-read-tree */
+                               assert(0 && "This case must be handled by git-read-tree");
+                       }
+               } else {
+                       const char *new_path1, *new_path2;
+                       clean_merge = 0;
+                       new_path1 = unique_path(path, branch1);
+                       new_path2 = unique_path(path, branch2);
+                       output("CONFLICT (add/add): File %s added non-identically "
+                              "in both branches. Adding as %s and %s instead.",
+                              path, new_path1, new_path2);
+                       remove_file(0, path);
+                       update_file(0, a_sha, a_mode, new_path1);
+                       update_file(0, b_sha, b_mode, new_path2);
+               }
+
+       } else if (o_sha && a_sha && b_sha) {
+               /* case D: Modified in both, but differently. */
+               struct merge_file_info mfi;
+               struct diff_filespec o, a, b;
+
+               output("Auto-merging %s", path);
+               o.path = a.path = b.path = (char *)path;
+               hashcpy(o.sha1, o_sha);
+               o.mode = o_mode;
+               hashcpy(a.sha1, a_sha);
+               a.mode = a_mode;
+               hashcpy(b.sha1, b_sha);
+               b.mode = b_mode;
+
+               mfi = merge_file(&o, &a, &b,
+                                branch1, branch2);
+
+               if (mfi.clean)
+                       update_file(1, mfi.sha, mfi.mode, path);
+               else {
+                       clean_merge = 0;
+                       output("CONFLICT (content): Merge conflict in %s", path);
+
+                       if (index_only)
+                               update_file(0, mfi.sha, mfi.mode, path);
+                       else
+                               update_file_flags(mfi.sha, mfi.mode, path,
+                                             0 /* update_cache */, 1 /* update_working_directory */);
+               }
+       } else
+               die("Fatal merge failure, shouldn't happen.");
+
+       if (cache_dirty)
+               flush_cache();
+
+       return clean_merge;
+}
+
+static int merge_trees(struct tree *head,
+                      struct tree *merge,
+                      struct tree *common,
+                      const char *branch1,
+                      const char *branch2,
+                      struct tree **result)
+{
+       int code, clean;
+       if (sha_eq(common->object.sha1, merge->object.sha1)) {
+               output("Already uptodate!");
+               *result = head;
+               return 1;
+       }
+
+       code = git_merge_trees(index_only, common, head, merge);
+
+       if (code != 0)
+               die("merging of trees %s and %s failed",
+                   sha1_to_hex(head->object.sha1),
+                   sha1_to_hex(merge->object.sha1));
+
+       *result = git_write_tree();
+
+       if (!*result) {
+               struct path_list *entries, *re_head, *re_merge;
+               int i;
+               path_list_clear(&current_file_set, 1);
+               path_list_clear(&current_directory_set, 1);
+               get_files_dirs(head);
+               get_files_dirs(merge);
+
+               entries = get_unmerged();
+               re_head  = get_renames(head, common, head, merge, entries);
+               re_merge = get_renames(merge, common, head, merge, entries);
+               clean = process_renames(re_head, re_merge,
+                               branch1, branch2);
+               for (i = 0; i < entries->nr; i++) {
+                       const char *path = entries->items[i].path;
+                       struct stage_data *e = entries->items[i].util;
+                       if (e->processed)
+                               continue;
+                       if (!process_entry(path, e, branch1, branch2))
+                               clean = 0;
+               }
+
+               path_list_clear(re_merge, 0);
+               path_list_clear(re_head, 0);
+               path_list_clear(entries, 1);
+
+               if (clean || index_only)
+                       *result = git_write_tree();
+               else
+                       *result = NULL;
+       } else {
+               clean = 1;
+               printf("merging of trees %s and %s resulted in %s\n",
+                      sha1_to_hex(head->object.sha1),
+                      sha1_to_hex(merge->object.sha1),
+                      sha1_to_hex((*result)->object.sha1));
+       }
+
+       return clean;
+}
+
+static struct commit_list *reverse_commit_list(struct commit_list *list)
+{
+       struct commit_list *next = NULL, *current, *backup;
+       for (current = list; current; current = backup) {
+               backup = current->next;
+               current->next = next;
+               next = current;
+       }
+       return next;
+}
+
+/*
+ * Merge the commits h1 and h2, return the resulting virtual
+ * commit object and a flag indicating the cleaness of the merge.
+ */
+static
+int merge(struct commit *h1,
+                         struct commit *h2,
+                         const char *branch1,
+                         const char *branch2,
+                         int call_depth /* =0 */,
+                         struct commit *ancestor /* =None */,
+                         struct commit **result)
+{
+       struct commit_list *ca = NULL, *iter;
+       struct commit *merged_common_ancestors;
+       struct tree *mrtree;
+       int clean;
+
+       output("Merging:");
+       output_commit_title(h1);
+       output_commit_title(h2);
+
+       if (ancestor)
+               commit_list_insert(ancestor, &ca);
+       else
+               ca = reverse_commit_list(get_merge_bases(h1, h2, 1));
+
+       output("found %u common ancestor(s):", commit_list_count(ca));
+       for (iter = ca; iter; iter = iter->next)
+               output_commit_title(iter->item);
+
+       merged_common_ancestors = pop_commit(&ca);
+       if (merged_common_ancestors == NULL) {
+               /* if there is no common ancestor, make an empty tree */
+               struct tree *tree = xcalloc(1, sizeof(struct tree));
+               unsigned char hdr[40];
+               int hdrlen;
+
+               tree->object.parsed = 1;
+               tree->object.type = OBJ_TREE;
+               write_sha1_file_prepare(NULL, 0, tree_type, tree->object.sha1,
+                                       hdr, &hdrlen);
+               merged_common_ancestors = make_virtual_commit(tree, "ancestor");
+       }
+
+       for (iter = ca; iter; iter = iter->next) {
+               output_indent = call_depth + 1;
+               /*
+                * When the merge fails, the result contains files
+                * with conflict markers. The cleanness flag is
+                * ignored, it was never acutally used, as result of
+                * merge_trees has always overwritten it: the commited
+                * "conflicts" were already resolved.
+                */
+               merge(merged_common_ancestors, iter->item,
+                     "Temporary merge branch 1",
+                     "Temporary merge branch 2",
+                     call_depth + 1,
+                     NULL,
+                     &merged_common_ancestors);
+               output_indent = call_depth;
+
+               if (!merged_common_ancestors)
+                       die("merge returned no commit");
+       }
+
+       if (call_depth == 0) {
+               setup_index(0 /* $GIT_DIR/index */);
+               index_only = 0;
+       } else {
+               setup_index(1 /* temporary index */);
+               git_read_tree(h1->tree);
+               index_only = 1;
+       }
+
+       clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
+                           branch1, branch2, &mrtree);
+
+       if (!ancestor && (clean || index_only)) {
+               *result = make_virtual_commit(mrtree, "merged tree");
+               commit_list_insert(h1, &(*result)->parents);
+               commit_list_insert(h2, &(*result)->parents->next);
+       } else
+               *result = NULL;
+
+       return clean;
+}
+
+static struct commit *get_ref(const char *ref)
+{
+       unsigned char sha1[20];
+       struct object *object;
+
+       if (get_sha1(ref, sha1))
+               die("Could not resolve ref '%s'", ref);
+       object = deref_tag(parse_object(sha1), ref, strlen(ref));
+       if (object->type != OBJ_COMMIT)
+               return NULL;
+       if (parse_commit((struct commit *)object))
+               die("Could not parse commit '%s'", sha1_to_hex(object->sha1));
+       return (struct commit *)object;
+}
+
+int main(int argc, char *argv[])
+{
+       static const char *bases[2];
+       static unsigned bases_count = 0;
+       int i, clean;
+       const char *branch1, *branch2;
+       struct commit *result, *h1, *h2;
+
+       original_index_file = getenv("GIT_INDEX_FILE");
+
+       if (!original_index_file)
+               original_index_file = strdup(git_path("index"));
+
+       temporary_index_file = strdup(git_path("mrg-rcrsv-tmp-idx"));
+
+       if (argc < 4)
+               die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
+
+       for (i = 1; i < argc; ++i) {
+               if (!strcmp(argv[i], "--"))
+                       break;
+               if (bases_count < sizeof(bases)/sizeof(*bases))
+                       bases[bases_count++] = argv[i];
+       }
+       if (argc - i != 3) /* "--" "<head>" "<remote>" */
+               die("Not handling anything other than two heads merge.");
+
+       branch1 = argv[++i];
+       branch2 = argv[++i];
+       printf("Merging %s with %s\n", branch1, branch2);
+
+       h1 = get_ref(branch1);
+       h2 = get_ref(branch2);
+
+       if (bases_count == 1) {
+               struct commit *ancestor = get_ref(bases[0]);
+               clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result);
+       } else
+               clean = merge(h1, h2, branch1, branch2, 0, NULL, &result);
+
+       if (cache_dirty)
+               flush_cache();
+
+       return clean ? 0: 1;
+}
+
+/*
+vim: sw=8 noet
+*/
index b1b8065851b2723a57e5d6ab0859ca72e52fd55f..b0034e4b217e12b267a51301c52b75e7255aec7b 100644 (file)
@@ -30,7 +30,7 @@ static void grow_refs_hash(void)
        int new_hash_size = (refs_hash_size + 1000) * 3 / 2;
        struct object_refs **new_hash;
 
-       new_hash = calloc(new_hash_size, sizeof(struct object_refs *));
+       new_hash = xcalloc(new_hash_size, sizeof(struct object_refs *));
        for (i = 0; i < refs_hash_size; i++) {
                struct object_refs *ref = refs_hash[i];
                if (!ref)
index 60bf16b902033d9f256282201ca7aeca74855d7e..92813001e25f20ae7dd7a1e0f0ef11ec5d746030 100644 (file)
--- a/object.c
+++ b/object.c
@@ -73,7 +73,7 @@ static void grow_object_hash(void)
        int new_hash_size = obj_hash_size < 32 ? 32 : 2 * obj_hash_size;
        struct object **new_hash;
 
-       new_hash = calloc(new_hash_size, sizeof(struct object *));
+       new_hash = xcalloc(new_hash_size, sizeof(struct object *));
        for (i = 0; i < obj_hash_size; i++) {
                struct object *obj = obj_hash[i];
                if (!obj)
index f15a10de37bc453c3b495ef8b3d3046d3611ce2f..b1ee72d1dc25085f2114748001d21e3e518e54ee 100644 (file)
@@ -85,8 +85,7 @@ void path_list_clear(struct path_list *list, int free_items)
                        for (i = 0; i < list->nr; i++) {
                                if (list->strdup_paths)
                                        free(list->items[i].path);
-                               if (list->items[i].util)
-                                       free(list->items[i].util);
+                               free(list->items[i].util);
                        }
                free(list->items);
        }
index b6982eac416fe765e1a570cdb25298e83264c81e..20c9d494ac94380c9074997e9cb3c69ff1c04a3f 100644 (file)
@@ -842,6 +842,23 @@ int read_cache_from(const char *path)
        die("index file corrupt");
 }
 
+int discard_cache()
+{
+       int ret;
+
+       active_nr = active_cache_changed = 0;
+       index_file_timestamp = 0;
+       cache_tree_free(&active_cache_tree);
+       if (cache_mmap == NULL)
+               return 0;
+       ret = munmap(cache_mmap, cache_mmap_size);
+       cache_mmap = NULL;
+       cache_mmap_size = 0;
+
+       /* no need to throw away allocated active_cache */
+       return ret;
+}
+
 #define WRITE_BUFFER_SIZE 8192
 static unsigned char write_buffer[WRITE_BUFFER_SIZE];
 static unsigned long write_buffer_len;
diff --git a/refs.c b/refs.c
index e70ef0ae0fe58671a35b71a55fbf81425ee84581..aab14fc107353099756403c1bf08a0786e184d98 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -348,10 +348,8 @@ void unlock_ref(struct ref_lock *lock)
                if (lock->lk)
                        rollback_lock_file(lock->lk);
        }
-       if (lock->ref_file)
-               free(lock->ref_file);
-       if (lock->log_file)
-               free(lock->log_file);
+       free(lock->ref_file);
+       free(lock->log_file);
        free(lock);
 }
 
index 769a80984d25ca362acb8d7c4f97ac3526c2b753..46272b591645e444396ae683673620594f336a7c 100644 (file)
@@ -917,23 +917,19 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
        enum object_type *type, unsigned long *sizep)
 {
        unsigned shift;
-       unsigned char *pack, c;
+       unsigned char c;
        unsigned long size;
 
        if (offset >= p->pack_size)
                die("object offset outside of pack file");
-
-       pack =  (unsigned char *) p->pack_base + offset;
-       c = *pack++;
-       offset++;
+       c = *((unsigned char *)p->pack_base + offset++);
        *type = (c >> 4) & 7;
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
                if (offset >= p->pack_size)
                        die("object offset outside of pack file");
-               c = *pack++;
-               offset++;
+               c = *((unsigned char *)p->pack_base + offset++);
                size += (c & 0x7f) << shift;
                shift += 7;
        }
@@ -996,16 +992,10 @@ void packed_object_info_detail(struct pack_entry *e,
        }
        switch (kind) {
        case OBJ_COMMIT:
-               strcpy(type, commit_type);
-               break;
        case OBJ_TREE:
-               strcpy(type, tree_type);
-               break;
        case OBJ_BLOB:
-               strcpy(type, blob_type);
-               break;
        case OBJ_TAG:
-               strcpy(type, tag_type);
+               strcpy(type, type_names[kind]);
                break;
        default:
                die("corrupted pack file %s containing object of kind %d",
@@ -1036,16 +1026,10 @@ static int packed_object_info(struct pack_entry *entry,
                unuse_packed_git(p);
                return retval;
        case OBJ_COMMIT:
-               strcpy(type, commit_type);
-               break;
        case OBJ_TREE:
-               strcpy(type, tree_type);
-               break;
        case OBJ_BLOB:
-               strcpy(type, blob_type);
-               break;
        case OBJ_TAG:
-               strcpy(type, tag_type);
+               strcpy(type, type_names[kind]);
                break;
        default:
                die("corrupted pack file %s containing object of kind %d",
@@ -1057,23 +1041,49 @@ static int packed_object_info(struct pack_entry *entry,
        return 0;
 }
 
-static void *unpack_delta_entry(unsigned char *base_sha1,
+static void *unpack_compressed_entry(struct packed_git *p,
+                                   unsigned long offset,
+                                   unsigned long size)
+{
+       int st;
+       z_stream stream;
+       unsigned char *buffer;
+
+       buffer = xmalloc(size + 1);
+       buffer[size] = 0;
+       memset(&stream, 0, sizeof(stream));
+       stream.next_in = (unsigned char*)p->pack_base + offset;
+       stream.avail_in = p->pack_size - offset;
+       stream.next_out = buffer;
+       stream.avail_out = size;
+
+       inflateInit(&stream);
+       st = inflate(&stream, Z_FINISH);
+       inflateEnd(&stream);
+       if ((st != Z_STREAM_END) || stream.total_out != size) {
+               free(buffer);
+               return NULL;
+       }
+
+       return buffer;
+}
+
+static void *unpack_delta_entry(struct packed_git *p,
+                               unsigned long offset,
                                unsigned long delta_size,
-                               unsigned long left,
                                char *type,
-                               unsigned long *sizep,
-                               struct packed_git *p)
+                               unsigned long *sizep)
 {
        struct pack_entry base_ent;
-       void *data, *delta_data, *result, *base;
-       unsigned long data_size, result_size, base_size;
-       z_stream stream;
-       int st;
+       void *delta_data, *result, *base;
+       unsigned long result_size, base_size;
+       unsigned char* base_sha1;
 
-       if (left < 20)
+       if ((offset + 20) >= p->pack_size)
                die("truncated pack file");
 
        /* The base entry _must_ be in the same pack */
+       base_sha1 = (unsigned char*)p->pack_base + offset;
        if (!find_pack_entry_one(base_sha1, &base_ent, p))
                die("failed to find delta-pack base object %s",
                    sha1_to_hex(base_sha1));
@@ -1082,23 +1092,7 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
                die("failed to read delta-pack base object %s",
                    sha1_to_hex(base_sha1));
 
-       data = base_sha1 + 20;
-       data_size = left - 20;
-       delta_data = xmalloc(delta_size);
-
-       memset(&stream, 0, sizeof(stream));
-
-       stream.next_in = data;
-       stream.avail_in = data_size;
-       stream.next_out = delta_data;
-       stream.avail_out = delta_size;
-
-       inflateInit(&stream);
-       st = inflate(&stream, Z_FINISH);
-       inflateEnd(&stream);
-       if ((st != Z_STREAM_END) || stream.total_out != delta_size)
-               die("delta data unpack failed");
-
+       delta_data = unpack_compressed_entry(p, offset + 20, delta_size);
        result = patch_delta(base, base_size,
                             delta_data, delta_size,
                             &result_size);
@@ -1110,33 +1104,6 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
        return result;
 }
 
-static void *unpack_non_delta_entry(unsigned char *data,
-                                   unsigned long size,
-                                   unsigned long left)
-{
-       int st;
-       z_stream stream;
-       unsigned char *buffer;
-
-       buffer = xmalloc(size + 1);
-       buffer[size] = 0;
-       memset(&stream, 0, sizeof(stream));
-       stream.next_in = data;
-       stream.avail_in = left;
-       stream.next_out = buffer;
-       stream.avail_out = size;
-
-       inflateInit(&stream);
-       st = inflate(&stream, Z_FINISH);
-       inflateEnd(&stream);
-       if ((st != Z_STREAM_END) || stream.total_out != size) {
-               free(buffer);
-               return NULL;
-       }
-
-       return buffer;
-}
-
 static void *unpack_entry(struct pack_entry *entry,
                          char *type, unsigned long *sizep)
 {
@@ -1157,36 +1124,23 @@ void *unpack_entry_gently(struct pack_entry *entry,
                          char *type, unsigned long *sizep)
 {
        struct packed_git *p = entry->p;
-       unsigned long offset, size, left;
-       unsigned char *pack;
+       unsigned long offset, size;
        enum object_type kind;
-       void *retval;
 
        offset = unpack_object_header(p, entry->offset, &kind, &size);
-       pack = (unsigned char *) p->pack_base + offset;
-       left = p->pack_size - offset;
        switch (kind) {
        case OBJ_DELTA:
-               retval = unpack_delta_entry(pack, size, left, type, sizep, p);
-               return retval;
+               return unpack_delta_entry(p, offset, size, type, sizep);
        case OBJ_COMMIT:
-               strcpy(type, commit_type);
-               break;
        case OBJ_TREE:
-               strcpy(type, tree_type);
-               break;
        case OBJ_BLOB:
-               strcpy(type, blob_type);
-               break;
        case OBJ_TAG:
-               strcpy(type, tag_type);
-               break;
+               strcpy(type, type_names[kind]);
+               *sizep = size;
+               return unpack_compressed_entry(p, offset, size);
        default:
                return NULL;
        }
-       *sizep = size;
-       retval = unpack_non_delta_entry(pack, size, left);
-       return retval;
 }
 
 int num_packed_objects(const struct packed_git *p)
@@ -1786,7 +1740,7 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size)
                        off += iret;
                        if (off == size) {
                                size *= 2;
-                               buf = realloc(buf, size);
+                               buf = xrealloc(buf, size);
                        }
                }
        } while (iret > 0);
index 69aebe600573fa0e014e97001f40836d3a18b82b..74f5c2a5755c8d29045498031c0bc3721a00b9c7 100755 (executable)
@@ -22,25 +22,64 @@ test_expect_success setup '
        tr "[mon]" '\''[\0\1\2]'\'' <file1 >file2 &&
 
        git commit -a -m second &&
+       git tag second &&
 
-       git diff --binary -R initial >patch
+       git diff --binary initial second >patch
 
 '
 
 test_expect_success 'apply in forward' '
 
+       T0=`git rev-parse "second^{tree}"` &&
+       git reset --hard initial &&
        git apply --index --binary patch &&
-       git diff initial >diff &&
-       diff -u /dev/null diff
-
+       T1=`git write-tree` &&
+       test "$T0" = "$T1"
 '
 
 test_expect_success 'apply in reverse' '
 
+       git reset --hard second &&
        git apply --reverse --binary --index patch &&
        git diff >diff &&
        diff -u /dev/null diff
 
 '
 
+test_expect_success 'setup separate repository lacking postimage' '
+
+       git tar-tree initial initial | tar xf - &&
+       (
+               cd initial && git init-db && git add .
+       ) &&
+
+       git tar-tree second second | tar xf - &&
+       (
+               cd second && git init-db && git add .
+       )
+
+'
+
+test_expect_success 'apply in forward without postimage' '
+
+       T0=`git rev-parse "second^{tree}"` &&
+       (
+               cd initial &&
+               git apply --index --binary ../patch &&
+               T1=`git write-tree` &&
+               test "$T0" = "$T1"
+       )
+'
+
+test_expect_success 'apply in reverse without postimage' '
+
+       T0=`git rev-parse "initial^{tree}"` &&
+       (
+               cd second &&
+               git apply --index --binary --reverse ../patch &&
+               T1=`git write-tree` &&
+               test "$T0" = "$T1"
+       )
+'
+
 test_done
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
new file mode 100755 (executable)
index 0000000..b4de075
--- /dev/null
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply with rejects
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
+       do
+               echo $i
+       done >file1 &&
+       cat file1 >saved.file1 &&
+       git update-index --add file1 &&
+       git commit -m initial &&
+
+       for i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21
+       do
+               echo $i
+       done >file1 &&
+       git diff >patch.1 &&
+       cat file1 >clean &&
+
+       for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21
+       do
+               echo $i
+       done >expected &&
+
+       mv file1 file2 &&
+       git update-index --add --remove file1 file2 &&
+       git diff -M HEAD >patch.2 &&
+
+       rm -f file1 file2 &&
+       mv saved.file1 file1 &&
+       git update-index --add --remove file1 file2 &&
+
+       for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21
+       do
+               echo $i
+       done >file1 &&
+
+       cat file1 >saved.file1
+'
+
+test_expect_success 'apply without --reject should fail' '
+
+       if git apply patch.1
+       then
+               echo "Eh? Why?"
+               exit 1
+       fi
+
+       diff -u file1 saved.file1
+'
+
+test_expect_success 'apply without --reject should fail' '
+
+       if git apply --verbose patch.1
+       then
+               echo "Eh? Why?"
+               exit 1
+       fi
+
+       diff -u file1 saved.file1
+'
+
+test_expect_success 'apply with --reject should fail but update the file' '
+
+       cat saved.file1 >file1 &&
+       rm -f file1.rej file2.rej &&
+
+       if git apply --reject patch.1
+       then
+               echo "succeeds with --reject?"
+               exit 1
+       fi
+
+       diff -u file1 expected &&
+
+       cat file1.rej &&
+
+       if test -f file2.rej
+       then
+               echo "file2 should not have been touched"
+               exit 1
+       fi
+'
+
+test_expect_success 'apply with --reject should fail but update the file' '
+
+       cat saved.file1 >file1 &&
+       rm -f file1.rej file2.rej file2 &&
+
+       if git apply --reject patch.2 >rejects
+       then
+               echo "succeeds with --reject?"
+               exit 1
+       fi
+
+       test -f file1 && {
+               echo "file1 still exists?"
+               exit 1
+       }
+       diff -u file2 expected &&
+
+       cat file2.rej &&
+
+       if test -f file1.rej
+       then
+               echo "file2 should not have been touched"
+               exit 1
+       fi
+
+'
+
+test_expect_success 'the same test with --verbose' '
+
+       cat saved.file1 >file1 &&
+       rm -f file1.rej file2.rej file2 &&
+
+       if git apply --reject --verbose patch.2 >rejects
+       then
+               echo "succeeds with --reject?"
+               exit 1
+       fi
+
+       test -f file1 && {
+               echo "file1 still exists?"
+               exit 1
+       }
+       diff -u file2 expected &&
+
+       cat file2.rej &&
+
+       if test -f file1.rej
+       then
+               echo "file2 should not have been touched"
+               exit 1
+       fi
+
+'
+
+test_expect_success 'apply cleanly with --verbose' '
+
+       git cat-file -p HEAD:file1 >file1 &&
+       rm -f file?.rej file2 &&
+
+       git apply --verbose patch.1 &&
+
+       diff -u file1 clean
+'
+
+test_done
index 6a82da73b6d3b6b8cca23c3183cddc36c5cafb4b..08602f522183dc43787616f37cba9b8af4e3dade 100644 (file)
@@ -69,9 +69,9 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
        for (i = 0; i < nbuf; i++) {
                if (mb[i].ptr[mb[i].size-1] != '\n') {
                        /* Incomplete line */
-                       priv->remainder = realloc(priv->remainder,
-                                                 priv->remainder_size +
-                                                 mb[i].size);
+                       priv->remainder = xrealloc(priv->remainder,
+                                                  priv->remainder_size +
+                                                  mb[i].size);
                        memcpy(priv->remainder + priv->remainder_size,
                               mb[i].ptr, mb[i].size);
                        priv->remainder_size += mb[i].size;
@@ -83,9 +83,9 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
                        consume_one(priv, mb[i].ptr, mb[i].size);
                        continue;
                }
-               priv->remainder = realloc(priv->remainder,
-                                         priv->remainder_size +
-                                         mb[i].size);
+               priv->remainder = xrealloc(priv->remainder,
+                                          priv->remainder_size +
+                                          mb[i].size);
                memcpy(priv->remainder + priv->remainder_size,
                       mb[i].ptr, mb[i].size);
                consume_one(priv, priv->remainder,