Merge branch 'fixes'
authorJunio C Hamano <junkio@cox.net>
Sat, 8 Oct 2005 00:06:21 +0000 (17:06 -0700)
committerJunio C Hamano <junkio@cox.net>
Sat, 8 Oct 2005 00:06:21 +0000 (17:06 -0700)
30 files changed:
.gitignore
Documentation/git-apply.txt
Documentation/git-applymbox.txt
Documentation/git-format-patch.txt
Documentation/git-mailsplit.txt
Documentation/git-tag.txt
Makefile
apply.c
cache.h
checkout-index.c
connect.c
daemon.c
diff.c
entry.c
git-am.sh [new file with mode: 0755]
git-applymbox.sh
git-applypatch.sh
git-format-patch.sh
git-ls-remote.sh
git-parse-remote.sh
git-shortlog.perl
git-tag.sh
git.sh
mailsplit.c
sha1_file.c
t/diff-lib.sh
t/t4000-diff-format.sh
t/t4001-diff-rename.sh
t/t4004-diff-rename-symlink.sh
update-index.c

index e90e2c3..d190c0a 100644 (file)
@@ -101,3 +101,4 @@ git-core-*/?*
 *.dsc
 *.deb
 git-core.spec
+*.exe
index 8cbbb4b..e095f93 100644 (file)
@@ -9,7 +9,7 @@ git-apply - Apply patch on a GIT index file and a work tree
 
 SYNOPSIS
 --------
-'git-apply' [--no-merge] [--stat] [--summary] [--check] [--index] [--show-files] [--apply] [<patch>...]
+'git-apply' [--stat] [--summary] [--check] [--index] [--show-files] [--apply] [<patch>...]
 
 DESCRIPTION
 -----------
@@ -22,11 +22,6 @@ OPTIONS
        The files to read patch from.  '-' can be used to read
        from the standard input.
 
---no-merge::
-       The default mode of operation is the merge behaviour
-       which is not implemented yet.  This flag explicitly
-       tells the program not to use the merge behaviour.
-
 --stat::
        Instead of applying the patch, output diffstat for the
        input.  Turns off "apply".
index bb54378..8f01ca6 100644 (file)
@@ -8,7 +8,7 @@ git-applymbox - Apply a series of patches in a mailbox
 
 SYNOPSIS
 --------
-'git-applymbox' [-u] [-k] [-q] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
+'git-applymbox' [-u] [-k] [-q] [-m] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
 
 DESCRIPTION
 -----------
@@ -33,6 +33,14 @@ OPTIONS
        munging, and is most useful when used to read back 'git
        format-patch --mbox' output.
 
+-m::
+       Patches are applied with `git-apply` command, and unless
+       it cleanly applies without fuzz, the processing fails.
+       With this flag, if a tree that the patch applies cleanly
+       is found in a repository, the patch is applied to the
+       tree and then a 3-way merge between the resulting tree
+       and the current tree.
+
 -u::
        By default, the commit log message, author name and
        author email are taken from the e-mail without any
index a1483ff..f3ef4c1 100644 (file)
@@ -8,7 +8,7 @@ git-format-patch - Prepare patches for e-mail submission.
 
 SYNOPSIS
 --------
-'git-format-patch' [-n][-o <dir>][-k][--mbox][--diff-options] <his> [<mine>]
+'git-format-patch' [-n][-o <dir>|--stdout][-k][--mbox][--diff-options] <his> [<mine>]
 
 DESCRIPTION
 -----------
@@ -54,6 +54,10 @@ OPTIONS
        concatenated together and fed to `git-applymbox`.
        Implies --author and --date.
 
+--stdout::
+       This flag generates the mbox formatted output to the
+       standard output, instead of saving them into a file per
+       patch and implies --mbox.
 
 Author
 ------
index 557d2e9..03a9477 100644 (file)
@@ -7,7 +7,7 @@ git-mailsplit - Totally braindamaged mbox splitter program.
 
 SYNOPSIS
 --------
-'git-mailsplit' <mbox> <directory>
+'git-mailsplit' [-d<prec>] [<mbox>] <directory>
 
 DESCRIPTION
 -----------
@@ -17,14 +17,23 @@ directory so you can process them further from there.
 OPTIONS
 -------
 <mbox>::
-       Mbox file to split.
+       Mbox file to split.  If not given, the mbox is read from
+       the standard input.
 
 <directory>::
        Directory in which to place the individual messages.
 
+-d<prec>::
+       Instead of the default 4 digits with leading zeros,
+       different precision can be specified for the generated
+       filenames.
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
+and Junio C Hamano <junkio@cox.net>
+
 
 Documentation
 --------------
index 1e0d4f5..1eafeef 100644 (file)
@@ -10,19 +10,27 @@ git-tag -  Create a tag object signed with GPG
 
 SYNOPSIS
 --------
-'git-tag' [-s | -a] [-f] <name>
+'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg>] <name>
 
 DESCRIPTION
 -----------
-Adds a "tag" reference in .git/refs/tags/
+Adds a 'tag' reference in .git/refs/tags/
 
-Unless "-f" is given, the tag must not yet exist in ".git/refs/tags"
+Unless `-f` is given, the tag must not yet exist in
+`.git/refs/tags/` directory.
 
-If "-s" or "-a" is passed, the user will be prompted for a tag message.
-and a tag object is created.  Otherwise just the SHA1 object
-name of the commit object is written.
+If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
+creates a 'tag' object, and requires the tag message.  Unless
+`-m <msg>` is given, an editor is started for the user to type
+in the tag message.
 
-A GnuPG signed tag object will be created when "-s" is used.
+Otherwise just the SHA1 object name of the commit object is
+written (i.e. an lightweight tag).
+
+A GnuPG signed tag object will be created when `-s` or `-u
+<key-id>` is used.  When `-u <key-id>` is not used, the
+committer identity for the current user is used to find the
+GnuPG key for signing.
 
 
 Author
index 92f0bda..1bdf4de 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,8 @@
 #
 # Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
 #
+# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
+#
 # Define COLLISION_CHECK below if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
 # sufficient guarantee that no collisions between objects will ever happen.
@@ -48,7 +50,7 @@
 
 # DEFINES += -DUSE_STDEV
 
-GIT_VERSION = 0.99.8
+GIT_VERSION = 0.99.8.GIT
 
 CFLAGS = -g -O2 -Wall
 ALL_CFLAGS = $(CFLAGS) $(PLATFORM_DEFINES) $(DEFINES)
@@ -83,7 +85,7 @@ SCRIPT_SH = \
        git-repack.sh git-request-pull.sh git-reset.sh \
        git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \
        git-tag.sh git-verify-tag.sh git-whatchanged.sh git.sh \
-       git-applymbox.sh git-applypatch.sh \
+       git-applymbox.sh git-applypatch.sh git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
        git-merge-resolve.sh git-grep.sh
 
@@ -96,31 +98,31 @@ SCRIPT_PYTHON = \
 
 # The ones that do not have to link with lcrypto nor lz.
 SIMPLE_PROGRAMS = \
-       git-get-tar-commit-id git-mailinfo git-mailsplit git-stripspace \
-       git-daemon git-var
+       git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \
+       git-stripspace$X git-var$X git-daemon$X
 
 # ... and all the rest
 PROGRAMS = \
-       git-apply git-cat-file \
-       git-checkout-index git-clone-pack git-commit-tree \
-       git-convert-objects git-diff-files \
-       git-diff-index git-diff-stages \
-       git-diff-tree git-fetch-pack git-fsck-objects \
-       git-hash-object git-init-db \
-       git-local-fetch git-ls-files git-ls-tree git-merge-base \
-       git-merge-index git-mktag git-pack-objects git-patch-id \
-       git-peek-remote git-prune-packed git-read-tree \
-       git-receive-pack git-rev-list git-rev-parse \
-       git-send-pack git-show-branch \
-       git-show-index git-ssh-fetch \
-       git-ssh-upload git-tar-tree git-unpack-file \
-       git-unpack-objects git-update-index git-update-server-info \
-       git-upload-pack git-verify-pack git-write-tree \
-       git-update-ref git-symbolic-ref \
+       git-apply$X git-cat-file$X \
+       git-checkout-index$X git-clone-pack$X git-commit-tree$X \
+       git-convert-objects$X git-diff-files$X \
+       git-diff-index$X git-diff-stages$X \
+       git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \
+       git-hash-object$X git-init-db$X \
+       git-local-fetch$X git-ls-files$X git-ls-tree$X git-merge-base$X \
+       git-merge-index$X git-mktag$X git-pack-objects$X git-patch-id$X \
+       git-peek-remote$X git-prune-packed$X git-read-tree$X \
+       git-receive-pack$X git-rev-list$X git-rev-parse$X \
+       git-send-pack$X git-show-branch$X \
+       git-show-index$X git-ssh-fetch$X \
+       git-ssh-upload$X git-tar-tree$X git-unpack-file$X \
+       git-unpack-objects$X git-update-index$X git-update-server-info$X \
+       git-upload-pack$X git-verify-pack$X git-write-tree$X \
+       git-update-ref$X git-symbolic-ref$X \
        $(SIMPLE_PROGRAMS)
 
 # Backward compatibility -- to be removed after 1.0
-PROGRAMS += git-ssh-pull git-ssh-push
+PROGRAMS += git-ssh-pull$X git-ssh-push$X
 
 GIT_LIST_TWEAK =
 
@@ -176,6 +178,13 @@ ifeq ($(shell uname -s),SunOS)
        TAR = gtar
        PLATFORM_DEFINES += -D__EXTENSIONS__
 endif
+ifeq ($(shell uname -o),Cygwin)
+       NO_STRCASESTR = YesPlease
+       NEEDS_LIBICONV = YesPlease
+       NO_IPV6 = YesPlease
+       X = .exe
+       PLATFORM_DEFINES += -DUSE_SYMLINK_HEAD=0
+endif
 ifneq (,$(findstring arm,$(shell uname -m)))
        ARM_SHA1 = YesPlease
 endif
@@ -192,7 +201,7 @@ ifndef NO_CURL
        else
                CURL_LIBCURL = -lcurl
        endif
-       PROGRAMS += git-http-fetch
+       PROGRAMS += git-http-fetch$X
 endif
 
 ifndef SHELL_PATH
@@ -249,6 +258,9 @@ ifdef NO_STRCASESTR
        DEFINES += -Dstrcasestr=gitstrcasestr
        LIB_OBJS += compat/strcasestr.o
 endif
+ifdef NO_IPV6
+       DEFINES += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
+endif
 
 ifdef PPC_SHA1
        SHA1_HEADER = "ppc/sha1.h"
@@ -287,25 +299,31 @@ git: git.sh Makefile
        rm -f $@+ $@
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+           -e 's/@@X@@/$(X)/g' \
            $(GIT_LIST_TWEAK) <$@.sh >$@+
        chmod +x $@+
        mv $@+ $@
 
 $(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh
        rm -f $@
-       sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' $@.sh >$@
+       sed -e '1s|#!.*/sh|#!$(SHELL_PATH)|' \
+           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+           $@.sh >$@
        chmod +x $@
 
 $(patsubst %.perl,%,$(SCRIPT_PERL)) : % : %.perl
        rm -f $@
-       sed -e '1s|#!.*perl|#!$(PERL_PATH)|' $@.perl >$@
+       sed -e '1s|#!.*perl|#!$(PERL_PATH)|' \
+           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+           $@.perl >$@
        chmod +x $@
 
 $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
        rm -f $@
        sed -e '1s|#!.*python|#!$(PYTHON_PATH)|' \
            -e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR)|g' \
-               $@.py >$@
+           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+           $@.py >$@
        chmod +x $@
 
 %.o: %.c
@@ -313,30 +331,30 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
 %.o: %.S
        $(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
-git-%: %.o $(LIB_FILE)
+git-%$X: %.o $(LIB_FILE)
        $(CC) $(ALL_CFLAGS) -o $@ $(filter %.o,$^) $(LIBS)
 
-git-mailinfo : SIMPLE_LIB += $(LIB_4_ICONV)
+git-mailinfo$X : SIMPLE_LIB += $(LIB_4_ICONV)
 $(SIMPLE_PROGRAMS) : $(LIB_FILE)
-$(SIMPLE_PROGRAMS) : git-% : %.o
+$(SIMPLE_PROGRAMS) : git-%$X : %.o
        $(CC) $(ALL_CFLAGS) -o $@ $(filter %.o,$^) $(LIB_FILE) $(SIMPLE_LIB)
 
-git-http-fetch: fetch.o
-git-local-fetch: fetch.o
-git-ssh-fetch: rsh.o fetch.o
-git-ssh-upload: rsh.o
-git-ssh-pull: rsh.o fetch.o
-git-ssh-push: rsh.o
+git-http-fetch$X: fetch.o
+git-local-fetch$X: fetch.o
+git-ssh-fetch$X: rsh.o fetch.o
+git-ssh-upload$X: rsh.o
+git-ssh-pull$X: rsh.o fetch.o
+git-ssh-push$X: rsh.o
 
-git-http-fetch: LIBS += $(CURL_LIBCURL)
-git-rev-list: LIBS += $(OPENSSL_LIBSSL)
+git-http-fetch$X: LIBS += $(CURL_LIBCURL)
+git-rev-list$X: LIBS += $(OPENSSL_LIBSSL)
 
 init-db.o: init-db.c
        $(CC) -c $(ALL_CFLAGS) \
                -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir)"' $*.c
 
 $(LIB_OBJS): $(LIB_H)
-$(patsubst git-%,%.o,$(PROGRAMS)): $(LIB_H)
+$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H)
 $(DIFF_OBJS): diffcore.h
 
 $(LIB_FILE): $(LIB_OBJS)
@@ -351,10 +369,10 @@ doc:
 test: all
        $(MAKE) -C t/ all
 
-test-date: test-date.c date.o
+test-date$X: test-date.c date.o
        $(CC) $(ALL_CFLAGS) -o $@ test-date.c date.o
 
-test-delta: test-delta.c diff-delta.o patch-delta.o
+test-delta$X: test-delta.c diff-delta.o patch-delta.o
        $(CC) $(ALL_CFLAGS) -o $@ $^
 
 check:
diff --git a/apply.c b/apply.c
index f886272..155fbe8 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -5,26 +5,17 @@
  *
  * This applies patches on top of some (arbitrary) version of the SCM.
  *
- * NOTE! It does all its work in the index file, and only cares about
- * the files in the working directory if you tell it to "merge" the
- * patch apply.
- *
- * Even when merging it always takes the source from the index, and
- * uses the working tree as a "branch" for a 3-way merge.
  */
 #include <ctype.h>
 #include <fnmatch.h>
 #include "cache.h"
 
-// We default to the merge behaviour, since that's what most people would
-// expect.
-//
 //  --check turns on checking that the working tree matches the
 //    files that are being modified, but doesn't apply the patch
 //  --stat does just a diffstat, and doesn't actually apply
 //  --show-files shows the directory changes
+//  --show-index-info shows the old and new index info for paths if available.
 //
-static int merge_patch = 1;
 static int check_index = 0;
 static int write_index = 0;
 static int diffstat = 0;
@@ -32,8 +23,9 @@ static int summary = 0;
 static int check = 0;
 static int apply = 1;
 static int show_files = 0;
+static int show_index_info = 0;
 static const char apply_usage[] =
-"git-apply [--no-merge] [--stat] [--summary] [--check] [--index] [--apply] [--show-files] <patch>...";
+"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] [--show-index-info] <patch>...";
 
 /*
  * For "diff-stat" like behaviour, we keep track of the biggest change
@@ -66,6 +58,8 @@ struct patch {
        struct fragment *fragments;
        char *result;
        unsigned long resultsize;
+       char old_sha1_prefix[41];
+       char new_sha1_prefix[41];
        struct patch *next;
 };
 
@@ -344,6 +338,38 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
        return 0;
 }
 
+static int gitdiff_index(const char *line, struct patch *patch)
+{
+       /* index line is N hexadecimal, "..", N hexadecimal,
+        * and optional space with octal mode.
+        */
+       const char *ptr, *eol;
+       int len;
+
+       ptr = strchr(line, '.');
+       if (!ptr || ptr[1] != '.' || 40 <= ptr - line)
+               return 0;
+       len = ptr - line;
+       memcpy(patch->old_sha1_prefix, line, len);
+       patch->old_sha1_prefix[len] = 0;
+
+       line = ptr + 2;
+       ptr = strchr(line, ' ');
+       eol = strchr(line, '\n');
+
+       if (!ptr || eol < ptr)
+               ptr = eol;
+       len = ptr - line;
+
+       if (40 <= len)
+               return 0;
+       memcpy(patch->new_sha1_prefix, line, len);
+       patch->new_sha1_prefix[len] = 0;
+       if (*ptr == ' ')
+               patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+       return 0;
+}
+
 /*
  * This is normal for a diff that doesn't change anything: we'll fall through
  * into the next diff. Tell the parser to break out.
@@ -448,6 +474,7 @@ static int parse_git_header(char *line, int len, unsigned int size, struct patch
                        { "rename to ", gitdiff_renamedst },
                        { "similarity index ", gitdiff_similarity },
                        { "dissimilarity index ", gitdiff_dissimilarity },
+                       { "index ", gitdiff_index },
                        { "", gitdiff_unrecognized },
                };
                int i;
@@ -676,7 +703,10 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                 /* We allow "\ No newline at end of file". Depending
                  * on locale settings when the patch was produced we
                  * don't know what this line looks like. The only
-                 * thing we do know is that it begins with "\ ". */
+                 * thing we do know is that it begins with "\ ".
+                * Checking for 12 is just for sanity check -- any
+                * l10n of "\ No newline..." is at least that long.
+                */
                case '\\':
                        if (len < 12 || memcmp(line, "\\ ", 2))
                                return -1;
@@ -1030,17 +1060,39 @@ static int check_patch(struct patch *patch)
 
        if (old_name) {
                int changed;
+               int stat_ret = lstat(old_name, &st);
 
-               if (lstat(old_name, &st) < 0)
-                       return error("%s: %s", old_name, strerror(errno));
                if (check_index) {
                        int pos = cache_name_pos(old_name, strlen(old_name));
                        if (pos < 0)
-                               return error("%s: does not exist in index", old_name);
+                               return error("%s: does not exist in index",
+                                            old_name);
+                       if (stat_ret < 0) {
+                               struct checkout costate;
+                               if (errno != ENOENT)
+                                       return error("%s: %s", old_name,
+                                                    strerror(errno));
+                               /* checkout */
+                               costate.base_dir = "";
+                               costate.base_dir_len = 0;
+                               costate.force = 0;
+                               costate.quiet = 0;
+                               costate.not_new = 0;
+                               costate.refresh_cache = 1;
+                               if (checkout_entry(active_cache[pos],
+                                                  &costate) ||
+                                   lstat(old_name, &st))
+                                       return -1;
+                       }
+
                        changed = ce_match_stat(active_cache[pos], &st);
                        if (changed)
-                               return error("%s: does not match index", old_name);
+                               return error("%s: does not match index",
+                                            old_name);
                }
+               else if (stat_ret < 0)
+                       return error("%s: %s", old_name, strerror(errno));
+
                if (patch->is_new < 0)
                        patch->is_new = 0;
                st.st_mode = ntohl(create_ce_mode(st.st_mode));
@@ -1121,6 +1173,36 @@ static void show_file_list(struct patch *patch)
        }
 }
 
+static inline int is_null_sha1(const unsigned char *sha1)
+{
+       return !memcmp(sha1, null_sha1, 20);
+}
+
+static void show_index_list(struct patch *list)
+{
+       struct patch *patch;
+
+       /* Once we start supporting the reverse patch, it may be
+        * worth showing the new sha1 prefix, but until then...
+        */
+       for (patch = list; patch; patch = patch->next) {
+               const unsigned char *sha1_ptr;
+               unsigned char sha1[20];
+               const char *name;
+
+               name = patch->old_name ? patch->old_name : patch->new_name;
+               if (patch->is_new)
+                       sha1_ptr = null_sha1;
+               else if (get_sha1(patch->old_sha1_prefix, sha1))
+                       die("sha1 information is lacking or useless (%s).",
+                           name);
+               else
+                       sha1_ptr = sha1;
+               printf("%06o %s %s\n",patch->old_mode,
+                      sha1_to_hex(sha1_ptr), name);
+       }
+}
+
 static void stat_patch_list(struct patch *patch)
 {
        int files, adds, dels;
@@ -1461,6 +1543,9 @@ static int apply_patch(int fd)
        if (show_files)
                show_file_list(list);
 
+       if (show_index_info)
+               show_index_list(list);
+
        if (diffstat)
                stat_patch_list(list);
 
@@ -1492,11 +1577,6 @@ int main(int argc, char **argv)
                        excludes = x;
                        continue;
                }
-               /* NEEDSWORK: this does not do anything at this moment. */
-               if (!strcmp(arg, "--no-merge")) {
-                       merge_patch = 0;
-                       continue;
-               }
                if (!strcmp(arg, "--stat")) {
                        apply = 0;
                        diffstat = 1;
@@ -1524,6 +1604,11 @@ int main(int argc, char **argv)
                        show_files = 1;
                        continue;
                }
+               if (!strcmp(arg, "--show-index-info")) {
+                       apply = 0;
+                       show_index_info = 1;
+                       continue;
+               }
                fd = open(arg, O_RDONLY);
                if (fd < 0)
                        usage(apply_usage);
diff --git a/cache.h b/cache.h
index ec2a161..514adb8 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -165,6 +165,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
 extern int ce_modified(struct cache_entry *ce, struct stat *st);
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
+extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 struct cache_file {
index f32513c..9784532 100644 (file)
@@ -63,15 +63,20 @@ static int checkout_file(const char *name)
 
 static int checkout_all(void)
 {
-       int i;
+       int i, errs = 0;
 
        for (i = 0; i < active_nr ; i++) {
                struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce))
                        continue;
                if (checkout_entry(ce, &state) < 0)
-                       return -1;
+                       errs++;
        }
+       if (errs)
+               /* we have already done our error reporting.
+                * exit with the same code as die().
+                */
+               exit(128);
        return 0;
 }
 
index 825c439..b157cf1 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -290,6 +290,8 @@ static enum protocol get_protocol(const char *name)
 #define STR_(s)        # s
 #define STR(s) STR_(s)
 
+#ifndef NO_IPV6
+
 static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
 {
        int sockfd = -1;
@@ -346,6 +348,77 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
        return 0;
 }
 
+#else /* NO_IPV6 */
+
+static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
+{
+       int sockfd = -1;
+       char *colon, *end;
+       char *port = STR(DEFAULT_GIT_PORT), *ep;
+       struct hostent *he;
+       struct sockaddr_in sa;
+       char **ap;
+       unsigned int nport;
+
+       if (host[0] == '[') {
+               end = strchr(host + 1, ']');
+               if (end) {
+                       *end = 0;
+                       end++;
+                       host++;
+               } else
+                       end = host;
+       } else
+               end = host;
+       colon = strchr(end, ':');
+
+       if (colon) {
+               *colon = 0;
+               port = colon + 1;
+       }
+
+
+       he = gethostbyname(host);
+       if (!he)
+               die("Unable to look up %s (%s)", host, hstrerror(h_errno));
+       nport = strtoul(port, &ep, 10);
+       if ( ep == port || *ep ) {
+               /* Not numeric */
+               struct servent *se = getservbyname(port,"tcp");
+               if ( !se )
+                       die("Unknown port %s\n", port);
+               nport = se->s_port;
+       }
+
+       for (ap = he->h_addr_list; *ap; ap++) {
+               sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
+               if (sockfd < 0)
+                       continue;
+
+               memset(&sa, 0, sizeof sa);
+               sa.sin_family = he->h_addrtype;
+               sa.sin_port = htons(nport);
+               memcpy(&sa.sin_addr, ap, he->h_length);
+
+               if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+                       close(sockfd);
+                       sockfd = -1;
+                       continue;
+               }
+               break;
+       }
+
+       if (sockfd < 0)
+               die("unable to connect a socket (%s)", strerror(errno));
+
+       fd[0] = sockfd;
+       fd[1] = sockfd;
+       packet_write(sockfd, "%s %s\n", prog, path);
+       return 0;
+}
+
+#endif /* NO_IPV6 */
+
 /*
  * Yeah, yeah, fixme. Need to pass in the heads etc.
  */
index 65e62d7..f285a8c 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -4,6 +4,7 @@
 #include <sys/wait.h>
 #include <sys/socket.h>
 #include <sys/time.h>
+#include <sys/poll.h>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -328,6 +329,7 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
                inet_ntop(AF_INET, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf));
                port = sin_addr->sin_port;
 
+#ifndef NO_IPV6
        } else if (addr->sa_family == AF_INET6) {
                struct sockaddr_in6 *sin6_addr = (void *) addr;
 
@@ -337,6 +339,7 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
                strcat(buf, "]");
 
                port = sin6_addr->sin6_port;
+#endif
        }
        loginfo("Connection from %s:%d", addrbuf, port);
 
@@ -369,16 +372,16 @@ static void child_handler(int signo)
        }
 }
 
-static int serve(int port)
+#ifndef NO_IPV6
+
+static int socksetup(int port, int **socklist_p)
 {
-       struct addrinfo hints, *ai0, *ai;
-       int gai;
        int socknum = 0, *socklist = NULL;
        int maxfd = -1;
-       fd_set fds_init, fds;
        char pbuf[NI_MAXSERV];
 
-       signal(SIGCHLD, child_handler);
+       struct addrinfo hints, *ai0, *ai;
+       int gai;
 
        sprintf(pbuf, "%d", port);
        memset(&hints, 0, sizeof(hints));
@@ -391,8 +394,6 @@ static int serve(int port)
        if (gai)
                die("getaddrinfo() failed: %s\n", gai_strerror(gai));
 
-       FD_ZERO(&fds_init);
-
        for (ai = ai0; ai; ai = ai->ai_next) {
                int sockfd;
                int *newlist;
@@ -431,23 +432,63 @@ static int serve(int port)
                socklist = newlist;
                socklist[socknum++] = sockfd;
 
-               FD_SET(sockfd, &fds_init);
                if (maxfd < sockfd)
                        maxfd = sockfd;
        }
 
        freeaddrinfo(ai0);
 
-       if (socknum == 0)
-               die("unable to allocate any listen sockets on port %u", port);
+       *socklist_p = socklist;
+       return socknum;
+}
+
+#else /* NO_IPV6 */
+
+static int socksetup(int port, int **socklist_p)
+{
+       struct sockaddr_in sin;
+       int sockfd;
+
+       sockfd = socket(AF_INET, SOCK_STREAM, 0);
+       if (sockfd < 0)
+               return 0;
+
+       memset(&sin, 0, sizeof sin);
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = htonl(INADDR_ANY);
+       sin.sin_port = htons(port);
+
+       if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
+               close(sockfd);
+               return 0;
+       }
 
+       *socklist_p = xmalloc(sizeof(int));
+       **socklist_p = sockfd;
+}
+
+#endif
+
+static int service_loop(int socknum, int *socklist)
+{
+       struct pollfd *pfd;
+       int i;
+
+       pfd = xcalloc(socknum, sizeof(struct pollfd));
+
+       for (i = 0; i < socknum; i++) {
+               pfd[i].fd = socklist[i];
+               pfd[i].events = POLLIN;
+       }
+
+       signal(SIGCHLD, child_handler);
+       
        for (;;) {
                int i;
-               fds = fds_init;
-               
-               if (select(maxfd + 1, &fds, NULL, NULL, NULL) < 0) {
+
+               if (poll(pfd, socknum, 0) < 0) {
                        if (errno != EINTR) {
-                               error("select failed, resuming: %s",
+                               error("poll failed, resuming: %s",
                                      strerror(errno));
                                sleep(1);
                        }
@@ -455,12 +496,10 @@ static int serve(int port)
                }
 
                for (i = 0; i < socknum; i++) {
-                       int sockfd = socklist[i];
-
-                       if (FD_ISSET(sockfd, &fds)) {
+                       if (pfd[i].revents & POLLIN) {
                                struct sockaddr_storage ss;
-                               int sslen = sizeof(ss);
-                               int incoming = accept(sockfd, (struct sockaddr *)&ss, &sslen);
+                               unsigned int sslen = sizeof(ss);
+                               int incoming = accept(pfd[i].fd, (struct sockaddr *)&ss, &sslen);
                                if (incoming < 0) {
                                        switch (errno) {
                                        case EAGAIN:
@@ -477,6 +516,17 @@ static int serve(int port)
        }
 }
 
+static int serve(int port)
+{
+       int socknum, *socklist;
+       
+       socknum = socksetup(port, &socklist);
+       if (socknum == 0)
+               die("unable to allocate any listen sockets on port %u", port);
+       
+       return service_loop(socknum, socklist);
+}      
+
 int main(int argc, char **argv)
 {
        int port = DEFAULT_GIT_PORT;
@@ -526,7 +576,7 @@ int main(int argc, char **argv)
        if (inetd_mode) {
                fclose(stderr); //FIXME: workaround
                return execute();
+       } else {
+               return serve(port);
        }
-
-       return serve(port);
 }
diff --git a/diff.c b/diff.c
index 7d06b03..cbb8632 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -596,15 +596,31 @@ static void run_external_diff(const char *pgm,
        remove_tempfile();
 }
 
+static void diff_fill_sha1_info(struct diff_filespec *one)
+{
+       if (DIFF_FILE_VALID(one)) {
+               if (!one->sha1_valid) {
+                       struct stat st;
+                       if (stat(one->path, &st) < 0)
+                               die("stat %s", one->path);
+                       if (index_path(one->sha1, one->path, &st, 0))
+                               die("cannot hash %s\n", one->path);
+               }
+       }
+       else
+               memset(one->sha1, 0, 20);
+}
+
 static void run_diff(struct diff_filepair *p)
 {
        const char *pgm = external_diff();
-       char msg_[PATH_MAX*2+200], *xfrm_msg;
+       char msg[PATH_MAX*2+300], *xfrm_msg;
        struct diff_filespec *one;
        struct diff_filespec *two;
        const char *name;
        const char *other;
        int complete_rewrite = 0;
+       int len;
 
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
@@ -616,39 +632,60 @@ static void run_diff(struct diff_filepair *p)
        name = p->one->path;
        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
        one = p->one; two = p->two;
+
+       diff_fill_sha1_info(one);
+       diff_fill_sha1_info(two);
+
+       len = 0;
        switch (p->status) {
        case DIFF_STATUS_COPIED:
-               sprintf(msg_,
-                       "similarity index %d%%\n"
-                       "copy from %s\n"
-                       "copy to %s",
-                       (int)(0.5 + p->score * 100.0/MAX_SCORE),
-                       name, other);
-               xfrm_msg = msg_;
+               len += snprintf(msg + len, sizeof(msg) - len,
+                               "similarity index %d%%\n"
+                               "copy from %s\n"
+                               "copy to %s\n",
+                               (int)(0.5 + p->score * 100.0/MAX_SCORE),
+                               name, other);
                break;
        case DIFF_STATUS_RENAMED:
-               sprintf(msg_,
-                       "similarity index %d%%\n"
-                       "rename from %s\n"
-                       "rename to %s",
-                       (int)(0.5 + p->score * 100.0/MAX_SCORE),
-                       name, other);
-               xfrm_msg = msg_;
+               len += snprintf(msg + len, sizeof(msg) - len,
+                               "similarity index %d%%\n"
+                               "rename from %s\n"
+                               "rename to %s\n",
+                               (int)(0.5 + p->score * 100.0/MAX_SCORE),
+                               name, other);
                break;
        case DIFF_STATUS_MODIFIED:
                if (p->score) {
-                       sprintf(msg_,
-                               "dissimilarity index %d%%",
-                               (int)(0.5 + p->score * 100.0/MAX_SCORE));
-                       xfrm_msg = msg_;
+                       len += snprintf(msg + len, sizeof(msg) - len,
+                                       "dissimilarity index %d%%\n",
+                                       (int)(0.5 + p->score *
+                                             100.0/MAX_SCORE));
                        complete_rewrite = 1;
                        break;
                }
                /* fallthru */
        default:
-               xfrm_msg = NULL;
+               /* nothing */
+               ;
        }
 
+       if (memcmp(one->sha1, two->sha1, 20)) {
+               char one_sha1[41];
+               memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
+
+               len += snprintf(msg + len, sizeof(msg) - len,
+                               "index %.7s..%.7s", one_sha1,
+                               sha1_to_hex(two->sha1));
+               if (one->mode == two->mode)
+                       len += snprintf(msg + len, sizeof(msg) - len,
+                                       " %06o", one->mode);
+               len += snprintf(msg + len, sizeof(msg) - len, "\n");
+       }
+
+       if (len)
+               msg[--len] = 0;
+       xfrm_msg = len ? msg : NULL;
+
        if (!pgm &&
            DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
            (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
diff --git a/entry.c b/entry.c
index b8426db..15b34eb 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -132,7 +132,7 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state)
                if (!state->force) {
                        if (!state->quiet)
                                fprintf(stderr, "git-checkout-index: %s already exists\n", path);
-                       return 0;
+                       return -1;
                }
 
                /*
diff --git a/git-am.sh b/git-am.sh
new file mode 100755 (executable)
index 0000000..9e41e70
--- /dev/null
+++ b/git-am.sh
@@ -0,0 +1,337 @@
+#!/bin/sh
+#
+#
+. git-sh-setup || die "Not a git archive"
+
+files=$(git-diff-index --cached --name-only HEAD) || exit
+if [ "$files" ]; then
+   echo "Dirty index: cannot apply patches (dirty: $files)" >&2
+   exit 1
+fi
+
+usage () {
+    echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--3way] <mbox>"
+    echo >&2 " or, when resuming"
+    echo >&2 " $0 [--skip]"
+    exit 1;
+}
+
+stop_here () {
+    echo "$1" >"$dotest/next"
+    exit 1
+}
+
+go_next () {
+       rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
+               "$dotest/patch" "$dotest/info"
+       echo "$next" >"$dotest/next"
+       this=$next
+}
+
+fall_back_3way () {
+    O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
+
+    rm -fr "$dotest"/patch-merge-*
+    mkdir "$dotest/patch-merge-tmp-dir"
+
+    # First see if the patch records the index info that we can use.
+    if git-apply --show-index-info "$dotest/patch" \
+       >"$dotest/patch-merge-index-info" 2>/dev/null &&
+       GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+       git-update-index --index-info <"$dotest/patch-merge-index-info" &&
+       GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+       git-write-tree >"$dotest/patch-merge-base+" &&
+       # index has the base tree now.
+       (
+           cd "$dotest/patch-merge-tmp-dir" &&
+           GIT_INDEX_FILE="../patch-merge-tmp-index" \
+           GIT_OBJECT_DIRECTORY="$O_OBJECT" \
+           git-apply --index <../patch
+        )
+    then
+       echo Using index info to reconstruct a base tree...
+       mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
+       mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
+    else
+       # Otherwise, try nearby trees that can be used to apply the
+       # patch.
+       (
+           N=10
+
+           # Hoping the patch is against our recent commits...
+           git-rev-list --max-count=$N HEAD
+
+           # or hoping the patch is against known tags...
+           git-ls-remote --tags .
+       ) |
+       while read base junk
+       do
+           # See if we have it as a tree...
+           git-cat-file tree "$base" >/dev/null 2>&1 || continue
+
+           rm -fr "$dotest"/patch-merge-* &&
+           mkdir "$dotest/patch-merge-tmp-dir" || break
+           (
+               cd "$dotest/patch-merge-tmp-dir" &&
+               GIT_INDEX_FILE=../patch-merge-tmp-index &&
+               GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
+               export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
+               git-read-tree "$base" &&
+               git-apply --index &&
+               mv ../patch-merge-tmp-index ../patch-merge-index &&
+               echo "$base" >../patch-merge-base
+           ) <"$dotest/patch"  2>/dev/null && break
+       done
+    fi
+
+    test -f "$dotest/patch-merge-index" &&
+    his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git-write-tree) &&
+    orig_tree=$(cat "$dotest/patch-merge-base") &&
+    rm -fr "$dotest"/patch-merge-* || exit 1
+
+    echo Falling back to patching base and 3-way merge...
+
+    # This is not so wrong.  Depending on which base we picked,
+    # orig_tree may be wildly different from ours, but his_tree
+    # has the same set of wildly different changes in parts the
+    # patch did not touch, so resolve ends up cancelling them,
+    # saying that we reverted all those changes.
+
+    git-merge-resolve $orig_tree -- HEAD $his_tree || {
+           echo Failed to merge in the changes.
+           exit 1
+    }
+}
+
+prec=4
+dotest=.dotest sign= utf8= keep= skip= interactive=
+
+while case "$#" in 0) break;; esac
+do
+       case "$1" in
+       -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
+       dotest=`expr "$1" : '-[^=]*=\(.*\)'`; shift ;;
+       -d|--d|--do|--dot|--dote|--dotes|--dotest)
+       case "$#" in 1) usage ;; esac; shift
+       dotest="$1"; shift;;
+
+       -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
+       --interacti|--interactiv|--interactive)
+       interactive=t; shift ;;
+
+       -3|--3|--3w|--3wa|--3way)
+       threeway=t; shift ;;
+       -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
+       sign=t; shift ;;
+       -u|--u|--ut|--utf|--utf8)
+       utf8=t; shift ;;
+       -k|--k|--ke|--kee|--keep)
+       keep=t; shift ;;
+
+       --sk|--ski|--skip)
+       skip=t; shift ;;
+
+       --)
+       shift; break ;;
+       -*)
+       usage ;;
+       *)
+       break ;;
+       esac
+done
+
+if test -d "$dotest" &&
+   last=$(cat "$dotest/last") &&
+   next=$(cat "$dotest/next") &&
+   test $# != 0 &&
+   test "$next" -gt "$last"
+then
+   rm -fr "$dotest"
+fi
+
+if test -d "$dotest"
+then
+       test ",$#," = ",0," ||
+       die "previous dotest directory $dotest still exists but mbox given."
+else
+       # Make sure we are not given --skip
+       test ",$skip," = ,, ||
+       die "we are not resuming."
+
+       # Start afresh.
+       mkdir -p "$dotest" || exit
+
+       # cat does the right thing for us, including '-' to mean
+       # standard input.
+       cat "$@" |
+       git-mailsplit -d$prec "$dotest/" >"$dotest/last" || {
+               rm -fr "$dotest"
+               exit 1
+       }
+
+       echo "$sign" >"$dotest/sign"
+       echo "$utf8" >"$dotest/utf8"
+       echo "$keep" >"$dotest/keep"
+       echo "$threeway" >"$dotest/3way"
+       echo 1 >"$dotest/next"
+fi
+
+if test "$(cat "$dotest/utf8")" = t
+then
+       utf8=-u
+fi
+if test "$(cat "$dotest/keep")" = t
+then
+       keep=-k
+fi
+if test "$(cat "$dotest/sign")" = t
+then
+       SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
+                       s/>.*/>/
+                       s/^/Signed-off-by: /'
+               `
+else
+       SIGNOFF=
+fi
+threeway=$(cat "$dotest/3way")
+
+last=`cat "$dotest/last"`
+this=`cat "$dotest/next"`
+if test "$skip" = t
+then
+       this=`expr "$this" + 1`
+fi
+
+if test "$this" -gt "$last"
+then
+       echo Nothing to do.
+       rm -fr "$dotest"
+       exit
+fi
+
+while test "$this" -le "$last"
+do
+       msgnum=`printf "%0${prec}d" $this`
+       next=`expr "$this" + 1`
+       test -f "$dotest/$msgnum" || {
+               go_next
+               continue
+       }
+       git-mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+               <"$dotest/$msgnum" >"$dotest/info" ||
+               stop_here $this
+       git-stripspace < "$dotest/msg" > "$dotest/msg-clean"
+
+       GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
+       GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
+       GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+       SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
+
+       case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
+       if test '' != "$SIGNOFF"
+       then
+               LAST_SIGNED_OFF_BY=`
+                       sed -ne '/^Signed-off-by: /p' "$dotest/msg-clean" |
+                       tail -n 1
+               `
+               ADD_SIGNOFF=$(test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
+                   test '' = "$LAST_SIGNED_OFF_BY" && echo
+                   echo "$SIGNOFF"
+               })
+       else
+               ADD_SIGNOFF=
+       fi
+       {
+               echo "$SUBJECT"
+               if test -s "$dotest/msg-clean"
+               then
+                       echo
+                       cat "$dotest/msg-clean"
+               fi
+               if test '' != "$ADD_SIGNOFF"
+               then
+                       echo "$ADD_SIGNOFF"
+               fi
+       } >"$dotest/final-commit"
+
+       if test "$interactive" = t
+       then
+           action=again
+           while test "$action" = again
+           do
+               echo "Commit Body is:"
+               echo "--------------------------"
+               cat "$dotest/final-commit"
+               echo "--------------------------"
+               echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
+               read reply
+               case "$reply" in
+               y*|Y*) action=yes ;;
+               a*|A*) action=yes interactive= ;;
+               n*|N*) action=skip ;;
+               e*|E*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
+                      action=again ;;
+               esac
+           done
+       else
+           action=yes
+       fi
+
+       if test $action = skip
+       then
+               go_next
+               continue
+       fi
+
+       if test -x "$GIT_DIR"/hooks/applypatch-msg
+       then
+               "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
+               stop_here $this
+       fi
+
+       echo
+       echo "Applying '$SUBJECT'"
+       echo
+
+       git-apply --index "$dotest/patch"; apply_status=$?
+       if test $apply_status = 1 && test "$threeway" = t
+       then
+               (fall_back_3way) || stop_here $this
+
+               # Applying the patch to an earlier tree and merging the
+               # result may have produced the same tree as ours.
+               if test '' = "$(git-diff-index --cached --name-only -z HEAD)"
+               then
+                       echo No changes -- Patch already applied.
+                       go_next
+                       continue
+               fi
+       fi
+       if test $apply_status != 0
+       then
+               echo Patch failed at $msgnum.
+               stop_here $this
+       fi
+
+       if test -x "$GIT_DIR"/hooks/pre-applypatch
+       then
+               "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
+       fi
+
+       tree=$(git-write-tree) &&
+       echo Wrote tree $tree &&
+       parent=$(git-rev-parse --verify HEAD) &&
+       commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
+       echo Committed: $commit &&
+       git-update-ref HEAD $commit $parent ||
+       stop_here $this
+
+       if test -x "$GIT_DIR"/hooks/post-applypatch
+       then
+               "$GIT_DIR"/hooks/post-applypatch
+       fi
+
+       go_next
+done
+
+rm -fr "$dotest"
index e2bfd02..4e77132 100755 (executable)
@@ -9,8 +9,6 @@
 ## You give it a mbox-format collection of emails, and it will try to
 ## apply them to the kernel using "applypatch"
 ##
-## applymbox [-u] [-k] [-q] (-c .dotest/msg-number | mail_archive) [Signoff_file]"
-##
 ## The patch application may fail in the middle.  In which case:
 ## (1) look at .dotest/patch and fix it up to apply
 ## (2) re-run applymbox with -c .dotest/msg-number for the current one.
@@ -21,7 +19,7 @@
 . git-sh-setup || die "Not a git archive"
 
 usage () {
-    echo >&2 "applymbox [-u] [-k] [-q] (-c .dotest/<num> | mbox) [signoff]"
+    echo >&2 "applymbox [-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]"
     exit 1
 }
 
@@ -33,6 +31,7 @@ do
        -k)     keep_subject=-k ;;
        -q)     query_apply=t ;;
        -c)     continue="$2"; resume=f; shift ;;
+       -m)     fallback_3way=t ;;
        -*)     usage ;;
        *)      break ;;
        esac
@@ -43,7 +42,8 @@ case "$continue" in
 '')
        rm -rf .dotest
        mkdir .dotest
-       git-mailsplit "$1" .dotest || exit 1
+       num_msgs=$(git-mailsplit "$1" .dotest) || exit 1
+       echo "$num_msgs patch(es) to process."
        shift
 esac
 
@@ -56,6 +56,9 @@ fi
 case "$query_apply" in
 t)     touch .dotest/.query_apply
 esac
+case "$fall_back_3way" in
+t)     : >.dotest/.3way
+esac
 case "$keep_subject" in
 -k)    : >.dotest/.keep_subject
 esac
@@ -80,7 +83,11 @@ do
     do
        git-applypatch .dotest/msg-clean .dotest/patch .dotest/info "$signoff"
        case "$?" in
-       0 | 2 )
+       0)
+               # Remove the cleanly applied one to reduce clutter.
+               rm -f .dotest/$i
+               ;;
+       2)
                # 2 is a special exit code from applypatch to indicate that
                # the patch wasn't applied, but continue anyway 
                ;;
index 9f5a45b..66fd19a 100755 (executable)
@@ -22,6 +22,8 @@ query_apply=.dotest/.query_apply
 ## if this file exists.
 keep_subject=.dotest/.keep_subject
 
+## We do not attempt the 3-way merge fallback unless this file exists.
+fall_back_3way=.dotest/.3way
 
 MSGFILE=$1
 PATCHFILE=$2
@@ -29,10 +31,10 @@ INFO=$3
 SIGNOFF=$4
 EDIT=${VISUAL:-${EDITOR:-vi}}
 
-export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' .dotest/info)"
-export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' .dotest/info)"
-export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' .dotest/info)"
-export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' .dotest/info)"
+export GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$INFO")"
+export GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$INFO")"
+export GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$INFO")"
+export SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$INFO")"
 
 if test '' != "$SIGNOFF"
 then
@@ -54,8 +56,10 @@ then
                        sed -ne '/^Signed-off-by: /p' "$MSGFILE" |
                        tail -n 1
                `
-               test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" ||
-               echo "$SIGNOFF" >>"$MSGFILE"
+               test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
+                   test '' = "$LAST_SIGNED_OFF_BY" && echo
+                   echo "$SIGNOFF"
+               } >>"$MSGFILE"
        fi
 fi
 
@@ -99,7 +103,81 @@ echo
 echo Applying "'$SUBJECT'"
 echo
 
-git-apply --index "$PATCHFILE" || exit 1
+git-apply --index "$PATCHFILE" || {
+
+       # git-apply exits with status 1 when the patch does not apply,
+       # but it die()s with other failures, most notably upon corrupt
+       # patch.  In the latter case, there is no point to try applying
+       # it to another tree and do 3-way merge.
+       test $? = 1 || exit 1
+
+       test -f "$fall_back_3way" || exit 1
+
+       # Here if we know which revision the patch applies to,
+       # we create a temporary working tree and index, apply the
+       # patch, and attempt 3-way merge with the resulting tree.
+
+       O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
+       rm -fr .patch-merge-*
+
+       (
+               N=10
+
+               # if the patch records the base tree...
+               sed -ne '
+                       /^diff /q
+                       /^applies-to: \([0-9a-f]*\)$/{
+                               s//\1/p
+                               q
+                       }
+               ' "$PATCHFILE"
+
+               # or hoping the patch is against our recent commits...
+               git-rev-list --max-count=$N HEAD
+
+               # or hoping the patch is against known tags...
+               git-ls-remote --tags .
+       ) |
+       while read base junk
+       do
+               # Try it if we have it as a tree.
+               git-cat-file tree "$base" >/dev/null 2>&1 || continue
+
+               rm -fr .patch-merge-tmp-* &&
+               mkdir .patch-merge-tmp-dir || break
+               (
+                       cd .patch-merge-tmp-dir &&
+                       GIT_INDEX_FILE=../.patch-merge-tmp-index &&
+                       GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
+                       export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
+                       git-read-tree "$base" &&
+                       git-apply --index &&
+                       mv ../.patch-merge-tmp-index ../.patch-merge-index &&
+                       echo "$base" >../.patch-merge-base
+               ) <"$PATCHFILE"  2>/dev/null && break
+       done
+
+       test -f .patch-merge-index &&
+       his_tree=$(GIT_INDEX_FILE=.patch-merge-index git-write-tree) &&
+       orig_tree=$(cat .patch-merge-base) &&
+       rm -fr .patch-merge-* || exit 1
+
+       echo Falling back to patching base and 3-way merge using $orig_tree...
+
+       # This is not so wrong.  Depending on which base we picked,
+       # orig_tree may be wildly different from ours, but his_tree
+       # has the same set of wildly different changes in parts the
+       # patch did not touch, so resolve ends up cancelling them,
+       # saying that we reverted all those changes.
+
+       if git-merge-resolve $orig_tree -- HEAD $his_tree
+       then
+               echo Done.
+       else
+               echo Failed to merge in the changes.
+               exit 1
+       fi
+}
 
 if test -x "$GIT_DIR"/hooks/pre-applypatch
 then
index 2844799..09b635e 100755 (executable)
@@ -6,7 +6,7 @@
 . git-sh-setup || die "Not a git archive."
 
 usage () {
-    echo >&2 "usage: $0"' [-n] [-o dir] [--keep-subject] [--mbox] [--check] [--signoff] [-<diff options>...] upstream [ our-head ]
+    echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox] [--check] [--signoff] [-<diff options>...] upstream [ our-head ]
 
 Prepare each commit with its patch since our-head forked from upstream,
 one file per patch, for e-mail submission.  Each output file is
@@ -49,6 +49,8 @@ do
     numbered=t ;;
     -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
     signoff=t ;;
+    --st|--std|--stdo|--stdou|--stdout)
+    stdout=t mbox=t date=t author=t ;;
     -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
     --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
     --output-direc=*|--output-direct=*|--output-directo=*|\
@@ -141,25 +143,7 @@ do
        esac
 done >$series
 
-total=`wc -l <$series | tr -dc "[0-9]"`
-i=1
-while read commit
-do
-    git-cat-file commit "$commit" | git-stripspace >$commsg
-    title=`sed -ne "$titleScript" <$commsg`
-    case "$numbered" in
-    '') num= ;;
-    *)
-       case $total in
-       1) num= ;;
-       *) num=' '`printf "%d/%d" $i $total` ;;
-       esac
-    esac
-
-    file=`printf '%04d-%stxt' $i "$title"`
-    i=`expr "$i" + 1`
-    echo "* $file"
-    {
+process_one () {
        mailScript='
        /./d
        /^$/n'
@@ -178,6 +162,7 @@ do
            echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
            ;;
        esac
+
        eval "$(sed -ne "$whosepatchScript" $commsg)"
        test "$author,$au" = ",$me" || {
                mailScript="$mailScript"'
@@ -196,7 +181,9 @@ Date: '"$ad"
        n
        b body'
 
-       sed -ne "$mailScript" <$commsg
+       (cat $commsg ; echo; echo) |
+       sed -ne "$mailScript" |
+       git-stripspace
 
        test "$signoff" = "t" && {
                offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
@@ -212,21 +199,49 @@ Date: '"$ad"
        echo
        git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
        echo
+       git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
        git-diff-tree -p $diff_opts "$commit"
+       echo "---"
+       echo "@@GIT_VERSION@@"
 
        case "$mbox" in
        t)
                echo
                ;;
        esac
-    } >"$outdir$file"
-    case "$check" in
-    t)
-       # This is slightly modified from Andrew Morton's Perfect Patch.
-       # Lines you introduce should not have trailing whitespace.
-       # Also check for an indentation that has SP before a TAB.
-        grep -n '^+\([         ]*      .*\|.*[         ]\)$' "$outdir$file"
-
-       : do not exit with non-zero because we saw no problem in the last one.
+}
+
+total=`wc -l <$series | tr -dc "[0-9]"`
+i=1
+while read commit
+do
+    git-cat-file commit "$commit" | git-stripspace >$commsg
+    title=`sed -ne "$titleScript" <$commsg`
+    case "$numbered" in
+    '') num= ;;
+    *)
+       case $total in
+       1) num= ;;
+       *) num=' '`printf "%d/%d" $i $total` ;;
+       esac
     esac
+
+    file=`printf '%04d-%stxt' $i "$title"`
+    if test '' = "$stdout"
+    then
+           echo "* $file"
+           process_one >"$outdir$file"
+           if test t = "$check"
+           then
+               # This is slightly modified from Andrew Morton's Perfect Patch.
+               # Lines you introduce should not have trailing whitespace.
+               # Also check for an indentation that has SP before a TAB.
+               grep -n '^+\([  ]*      .*\|.*[         ]\)$' "$outdir$file"
+               :
+           fi
+    else
+           echo >&2 "* $file"
+           process_one
+    fi
+    i=`expr "$i" + 1`
 done <$series
index bfbd5a4..f0f0b07 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
 
 usage () {
     echo >&2 "usage: $0 [--heads] [--tags] <repository> <refs>..."
index 4d8a572..5e75e15 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-. git-sh-setup || die "Not a git archive"
+. git-sh-setup
 
 get_data_source () {
        case "$1" in
index 8f0984b..0b14f83 100755 (executable)
@@ -2,55 +2,13 @@
 
 use strict;
 
-#
-# Even with git, we don't always have name translations.
-# So have an email->real name table to translate the
-# (hopefully few) missing names
-#
-my %mailmap = (
-       'R.Marek@sh.cvut.cz' => 'Rudolf Marek',
-       'Ralf.Wildenhues@gmx.de' => 'Ralf Wildenhues',
-       'aherrman@de.ibm.com' => 'Andreas Herrmann',
-       'akpm@osdl.org' => 'Andrew Morton',
-       'andrew.vasquez@qlogic.com' => 'Andrew Vasquez',
-       'aquynh@gmail.com' => 'Nguyen Anh Quynh',
-       'axboe@suse.de' => 'Jens Axboe',
-       'blaisorblade@yahoo.it' => 'Paolo \'Blaisorblade\' Giarrusso',
-       'bunk@stusta.de' => 'Adrian Bunk',
-       'domen@coderock.org' => 'Domen Puncer',
-       'dougg@torque.net' => 'Douglas Gilbert',
-       'dwmw2@shinybook.infradead.org' => 'David Woodhouse',
-       'ecashin@coraid.com' => 'Ed L Cashin',
-       'felix@derklecks.de' => 'Felix Moeller',
-       'fzago@systemfabricworks.com' => 'Frank Zago',
-       'gregkh@suse.de' => 'Greg Kroah-Hartman',
-       'hch@lst.de' => 'Christoph Hellwig',
-       'htejun@gmail.com' => 'Tejun Heo',
-       'jejb@mulgrave.(none)' => 'James Bottomley',
-       'jejb@titanic.il.steeleye.com' => 'James Bottomley',
-       'jgarzik@pretzel.yyz.us' => 'Jeff Garzik',
-       'johnpol@2ka.mipt.ru' => 'Evgeniy Polyakov',
-       'kay.sievers@vrfy.org' => 'Kay Sievers',
-       'minyard@acm.org' => 'Corey Minyard',
-       'mshah@teja.com' => 'Mitesh shah',
-       'pj@ludd.ltu.se' => 'Peter A Jonsson',
-       'rmps@joel.ist.utl.pt' => 'Rui Saraiva',
-       'santtu.hyrkko@gmail.com' => 'Santtu Hyrkkö',
-       'simon@thekelleys.org.uk' => 'Simon Kelley',
-       'ssant@in.ibm.com' => 'Sachin P Sant',
-       'terra@gnome.org' => 'Morten Welinder',
-       'tony.luck@intel.com' => 'Tony Luck',
-       'welinder@anemone.rentec.com' => 'Morten Welinder',
-       'welinder@darter.rentec.com' => 'Morten Welinder',
-       'welinder@troll.com' => 'Morten Welinder',
-);
-
+my (%mailmap);
+my (%email);
 my (%map);
 my $pstate = 1;
 my $n_records = 0;
 my $n_output = 0;
 
-
 sub shortlog_entry($$) {
        my ($name, $desc) = @_;
        my $key = $name;
@@ -108,41 +66,35 @@ sub changelog_input {
                if ($pstate == 1) {
                        my ($email);
 
-                       next unless /^[Aa]uthor:? (.*)<(.*)>.*$/;
-       
+                       next unless /^[Aa]uthor:?\s*(.*?)\s*<(.*)>/;
+
                        $n_records++;
-       
+
                        $author = $1;
                        $email = $2;
                        $desc = undef;
 
-                       # trim trailing whitespace.
-                       # why doesn't chomp work?
-                       while ($author && ($author =~ /\s$/)) {
-                               chop $author;
-                       }
-       
                        # cset author fixups
                        if (exists $mailmap{$email}) {
                                $author = $mailmap{$email};
                        } elsif (exists $mailmap{$author}) {
                                $author = $mailmap{$author};
-                       } elsif ((!$author) || ($author eq "")) {
+                       } elsif (!$author) {
                                $author = $email;
                        }
-       
+                       $email{$author}{$email}++;
                        $pstate++;
                }
-       
+
                # skip to blank line
                elsif ($pstate == 2) {
                        next unless /^\s*$/;
                        $pstate++;
                }
-       
+
                # skip to non-blank line
                elsif ($pstate == 3) {
-                       next unless /^\s*(\S.*)$/;
+                       next unless /^\s*?(.*)/;
 
                        # skip lines that are obviously not
                        # a 1-line cset description
@@ -150,9 +102,9 @@ sub changelog_input {
 
                        chomp;
                        $desc = $1;
-       
+
                        &shortlog_entry($author, $desc);
-       
+
                        $pstate = 1;
                }
        
@@ -162,16 +114,87 @@ sub changelog_input {
        }
 }
 
+sub read_mailmap {
+       my ($fh, $mailmap) = @_;
+       while (<$fh>) {
+               chomp;
+               if (/^([^#].*?)\s*<(.*)>/) {
+                       $mailmap->{$2} = $1;
+               }
+       }
+}
+
+sub setup_mailmap {
+       read_mailmap(\*DATA, \%mailmap);
+       if (-f '.mailmap') {
+               my $fh = undef;
+               open $fh, '<', '.mailmap';
+               read_mailmap($fh, \%mailmap);
+               close $fh;
+       }
+}
+
 sub finalize {
        #print "\n$n_records records parsed.\n";
 
        if ($n_records != $n_output) {
                die "parse error: input records != output records\n";
        }
+       if (0) {
+               for my $author (sort keys %email) {
+                       my $e = $email{$author};
+                       for my $email (sort keys %$e) {
+                               print STDERR "$author <$email>\n";
+                       }
+               }
+       }
 }
 
+&setup_mailmap;
 &changelog_input;
 &shortlog_output;
 &finalize;
 exit(0);
 
+
+__DATA__
+#
+# Even with git, we don't always have name translations.
+# So have an email->real name table to translate the
+# (hopefully few) missing names
+#
+Adrian Bunk <bunk@stusta.de>
+Andreas Herrmann <aherrman@de.ibm.com>
+Andrew Morton <akpm@osdl.org>
+Andrew Vasquez <andrew.vasquez@qlogic.com>
+Christoph Hellwig <hch@lst.de>
+Corey Minyard <minyard@acm.org>
+David Woodhouse <dwmw2@shinybook.infradead.org>
+Domen Puncer <domen@coderock.org>
+Douglas Gilbert <dougg@torque.net>
+Ed L Cashin <ecashin@coraid.com>
+Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+Felix Moeller <felix@derklecks.de>
+Frank Zago <fzago@systemfabricworks.com>
+Greg Kroah-Hartman <gregkh@suse.de>
+James Bottomley <jejb@mulgrave.(none)>
+James Bottomley <jejb@titanic.il.steeleye.com>
+Jeff Garzik <jgarzik@pretzel.yyz.us>
+Jens Axboe <axboe@suse.de>
+Kay Sievers <kay.sievers@vrfy.org>
+Mitesh shah <mshah@teja.com>
+Morten Welinder <terra@gnome.org>
+Morten Welinder <welinder@anemone.rentec.com>
+Morten Welinder <welinder@darter.rentec.com>
+Morten Welinder <welinder@troll.com>
+Nguyen Anh Quynh <aquynh@gmail.com>
+Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
+Peter A Jonsson <pj@ludd.ltu.se>
+Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+Rudolf Marek <R.Marek@sh.cvut.cz>
+Rui Saraiva <rmps@joel.ist.utl.pt>
+Sachin P Sant <ssant@in.ibm.com>
+Santtu Hyrkk\e,Av\e(B <santtu.hyrkko@gmail.com>
+Simon Kelley <simon@thekelleys.org.uk>
+Tejun Heo <htejun@gmail.com>
+Tony Luck <tony.luck@intel.com>
index 76c1bcd..9afdf5c 100755 (executable)
@@ -4,7 +4,7 @@
 . git-sh-setup || die "Not a git archive"
 
 usage () {
-    echo >&2 "Usage: git-tag [-a | -s] [-f] [-m "tag message"] tagname"
+    echo >&2 "Usage: git-tag [-a | -s | -u <key-id>] [-f] [-m <msg>] <tagname>"
     exit 1
 }
 
@@ -12,6 +12,7 @@ annotate=
 signed=
 force=
 message=
+username=
 while case "$#" in 0) break ;; esac
 do
     case "$1" in
@@ -30,6 +31,12 @@ do
        shift
        message="$1"
        ;;
+    -u)
+       annotate=1
+       signed=1
+       shift
+       username="$1"
+       ;;
     -*)
         usage
        ;;
@@ -50,6 +57,7 @@ shift
 object=$(git-rev-parse --verify --default HEAD "$@") || exit 1
 type=$(git-cat-file -t $object) || exit 1
 tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
+: ${username:=$(expr "$tagger" : '\(.*>\)')}
 
 trap 'rm -f .tmp-tag* .tagmsg .editmsg' 0
 
@@ -65,13 +73,15 @@ if [ "$annotate" ]; then
 
     grep -v '^#' < .editmsg | git-stripspace > .tagmsg
 
-    [ -s .tagmsg ] || exit
+    [ -s .tagmsg ] || {
+       echo >&2 "No tag message?"
+       exit 1
+    }
 
     ( echo -e "object $object\ntype $type\ntag $name\ntagger $tagger\n"; cat .tagmsg ) > .tmp-tag
     rm -f .tmp-tag.asc .tagmsg
     if [ "$signed" ]; then
-       me=$(expr "$tagger" : '\(.*>\)') &&
-       gpg -bsa -u "$me" .tmp-tag &&
+       gpg -bsa -u "$username" .tmp-tag &&
        cat .tmp-tag.asc >>.tmp-tag ||
        die "failed to sign the tag with GPG."
     fi
diff --git a/git.sh b/git.sh
index dc383ed..7400c16 100755 (executable)
--- a/git.sh
+++ b/git.sh
@@ -11,7 +11,17 @@ case "$#" in
                echo "git version @@GIT_VERSION@@"
                exit 0 ;;
        esac
-       test -x $path/git-$cmd && exec $path/git-$cmd "$@" ;;
+       
+       test -x $path/git-$cmd && exec $path/git-$cmd "$@"
+       
+       case '@@X@@' in
+           '')
+               ;;
+           *)
+               test -x $path/git-$cmd@@X@@ && exec $path/git-$cmd@@X@@ "$@"
+               ;;
+       esac
+       ;;
 esac
 
 echo "Usage: git COMMAND [OPTIONS] [TARGET]"
index 7afea1a..7981f87 100644 (file)
 #include <stdio.h>
 #include <ctype.h>
 #include <assert.h>
+#include "cache.h"
 
-static int usage(void)
-{
-       fprintf(stderr, "mailsplit <mbox> <directory>\n");
-       exit(1);
-}
-
-static int linelen(const char *map, unsigned long size)
-{
-       int len = 0, c;
-
-       do {
-               c = *map;
-               map++;
-               size--;
-               len++;
-       } while (size && c != '\n');
-       return len;
-}
+static const char git_mailsplit_usage[] =
+"git-mailsplit [-d<prec>] [<mbox>] <directory>";
 
 static int is_from_line(const char *line, int len)
 {
@@ -65,81 +50,110 @@ static int is_from_line(const char *line, int len)
        return 1;
 }
 
-static int parse_email(const void *map, unsigned long size)
+/* Could be as small as 64, enough to hold a Unix "From " line. */
+static char buf[4096];
+
+/* Called with the first line (potentially partial)
+ * already in buf[] -- normally that should begin with
+ * the Unix "From " line.  Write it into the specified
+ * file.
+ */
+static int split_one(FILE *mbox, const char *name)
 {
-       unsigned long offset;
+       FILE *output = NULL;
+       int len = strlen(buf);
+       int fd;
+       int status = 0;
 
-       if (size < 6 || memcmp("From ", map, 5))
+       if (!is_from_line(buf, len))
                goto corrupt;
 
-       /* Make sure we don't trigger on this first line */
-       map++; size--; offset=1;
+       fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
+       if (fd < 0)
+               die("cannot open output file %s", name);
+       output = fdopen(fd, "w");
 
-       /*
-        * Search for a line beginning with "From ", and 
-        * having something that looks like a date format.
+       /* Copy it out, while searching for a line that begins with
+        * "From " and having something that looks like a date format.
         */
-       do {
-               int len = linelen(map, size);
-               if (is_from_line(map, len))
-                       return offset;
-               map += len;
-               size -= len;
-               offset += len;
-       } while (size);
-       return offset;
-
-corrupt:
+       for (;;) {
+               int is_partial = (buf[len-1] != '\n');
+
+               if (fputs(buf, output) == EOF)
+                       die("cannot write output");
+
+               if (fgets(buf, sizeof(buf), mbox) == NULL) {
+                       if (feof(mbox)) {
+                               status = 1;
+                               break;
+                       }
+                       die("cannot read mbox");
+               }
+               len = strlen(buf);
+               if (!is_partial && is_from_line(buf, len))
+                       break; /* done with one message */
+       }
+       fclose(output);
+       return status;
+
+ corrupt:
+       if (output)
+               fclose(output);
+       unlink(name);
        fprintf(stderr, "corrupt mailbox\n");
        exit(1);
 }
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-       int fd, nr;
-       struct stat st;
-       unsigned long size;
-       void *map;
-
-       if (argc != 3)
-               usage();
-       fd = open(argv[1], O_RDONLY);
-       if (fd < 0) {
-               perror(argv[1]);
-               exit(1);
-       }
-       if (chdir(argv[2]) < 0)
-               usage();
-       if (fstat(fd, &st) < 0) {
-               perror("stat");
-               exit(1);
+       int i, nr, nr_prec = 4;
+       FILE *mbox = NULL;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+
+               if (arg[0] != '-')
+                       break;
+               /* do flags here */
+               if (!strncmp(arg, "-d", 2)) {
+                       nr_prec = strtol(arg + 2, NULL, 10);
+                       if (nr_prec < 3 || 10 <= nr_prec)
+                               usage(git_mailsplit_usage);
+                       continue;
+               }
        }
-       size = st.st_size;
-       map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (map == MAP_FAILED) {
-               perror("mmap");
-               close(fd);
-               exit(1);
+
+       /* Either one remaining arg (dir), or two (mbox and dir) */
+       switch (argc - i) {
+       case 1:
+               mbox = stdin;
+               break;
+       case 2:
+               if ((mbox = fopen(argv[i], "r")) == NULL)
+                       die("cannot open mbox %s for reading", argv[i]);
+               break;
+       default:
+               usage(git_mailsplit_usage);
        }
-       close(fd);
+       if (chdir(argv[argc - 1]) < 0)
+               usage(git_mailsplit_usage);
+
        nr = 0;
-       do {
+       if (fgets(buf, sizeof(buf), mbox) == NULL)
+               die("cannot read mbox");
+
+       for (;;) {
                char name[10];
-               unsigned long len = parse_email(map, size);
-               assert(len <= size);
-               sprintf(name, "%04d", ++nr);
-               fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
-               if (fd < 0) {
-                       perror(name);
-                       exit(1);
-               }
-               if (write(fd, map, len) != len) {
-                       perror("write");
+
+               sprintf(name, "%0*d", nr_prec, ++nr);
+               switch (split_one(mbox, name)) {
+               case 0:
+                       break;
+               case 1:
+                       printf("%d\n", nr);
+                       return 0;
+               default:
                        exit(1);
                }
-               close(fd);
-               map += len;
-               size -= len;
-       } while (size > 0);
-       return 0;
+       }
 }
index 895c1fa..287f618 100644 (file)
@@ -1545,3 +1545,42 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
                munmap(buf, size);
        return ret;
 }
+
+int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object)
+{
+       int fd;
+       char *target;
+
+       switch (st->st_mode & S_IFMT) {
+       case S_IFREG:
+               fd = open(path, O_RDONLY);
+               if (fd < 0)
+                       return error("open(\"%s\"): %s", path,
+                                    strerror(errno));
+               if (index_fd(sha1, fd, st, write_object, NULL) < 0)
+                       return error("%s: failed to insert into database",
+                                    path);
+               break;
+       case S_IFLNK:
+               target = xmalloc(st->st_size+1);
+               if (readlink(path, target, st->st_size+1) != st->st_size) {
+                       char *errstr = strerror(errno);
+                       free(target);
+                       return error("readlink(\"%s\"): %s", path,
+                                    errstr);
+               }
+               if (!write_object) {
+                       unsigned char hdr[50];
+                       int hdrlen;
+                       write_sha1_file_prepare(target, st->st_size, "blob",
+                                               sha1, hdr, &hdrlen);
+               } else if (write_sha1_file(target, st->st_size, "blob", sha1))
+                       return error("%s: failed to insert into database",
+                                    path);
+               free(target);
+               break;
+       default:
+               return error("%s: unsupported file type", path);
+       }
+       return 0;
+}
index a912f43..745a1b0 100755 (executable)
@@ -29,7 +29,13 @@ compare_diff_raw_z () {
 compare_diff_patch () {
     # When heuristics are improved, the score numbers would change.
     # Ignore them while comparing.
-    sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$1" >.tmp-1
-    sed -e '/^[dis]*imilarity index [0-9]*%$/d' <"$2" >.tmp-2
+    sed -e '
+       /^[dis]*imilarity index [0-9]*%$/d
+       /^index [0-9a-f]*\.\.[0-9a-f]/d
+    ' <"$1" >.tmp-1
+    sed -e '
+       /^[dis]*imilarity index [0-9]*%$/d
+       /^index [0-9a-f]*\.\.[0-9a-f]/d
+    ' <"$2" >.tmp-2
     diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
 }
index f3b6330..beb6d8f 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Test built-in diff output engine.
 
 '
 . ./test-lib.sh
+. ../diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
@@ -48,6 +49,6 @@ EOF
 
 test_expect_success \
     'validate git-diff-files -p output.' \
-    'cmp -s current expected'
+    'compare_diff_patch current expected'
 
 test_done
index be47485..2e3c20d 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Test rename detection in diff engine.
 
 '
 . ./test-lib.sh
+. ../diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
@@ -61,6 +62,6 @@ EOF
 
 test_expect_success \
     'validate the output.' \
-    'diff -I "similarity.*" >/dev/null current expected'
+    'compare_diff_patch current expected'
 
 test_done
index f59614a..a23aaa0 100755 (executable)
@@ -10,6 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed
 by an edit for them.
 '
 . ./test-lib.sh
+. ../diff-lib.sh
 
 test_expect_success \
     'prepare reference tree' \
@@ -61,6 +62,6 @@ EOF
 
 test_expect_success \
     'validate diff output' \
-    'diff -u current expected'
+    'compare_diff_patch current expected'
 
 test_done
index b825a11..01b4088 100644 (file)
@@ -37,8 +37,6 @@ static int add_file_to_cache(const char *path)
        int size, namelen, option, status;
        struct cache_entry *ce;
        struct stat st;
-       int fd;
-       char *target;
 
        status = lstat(path, &st);
        if (status < 0 || S_ISDIR(st.st_mode)) {
@@ -77,34 +75,9 @@ static int add_file_to_cache(const char *path)
        fill_stat_cache_info(ce, &st);
        ce->ce_mode = create_ce_mode(st.st_mode);
        ce->ce_flags = htons(namelen);
-       switch (st.st_mode & S_IFMT) {
-       case S_IFREG:
-               fd = open(path, O_RDONLY);
-               if (fd < 0)
-                       return error("open(\"%s\"): %s", path, strerror(errno));
-               if (index_fd(ce->sha1, fd, &st, !info_only, NULL) < 0)
-                       return error("%s: failed to insert into database", path);
-               break;
-       case S_IFLNK:
-               target = xmalloc(st.st_size+1);
-               if (readlink(path, target, st.st_size+1) != st.st_size) {
-                       char *errstr = strerror(errno);
-                       free(target);
-                       return error("readlink(\"%s\"): %s", path,
-                                    errstr);
-               }
-               if (info_only) {
-                       unsigned char hdr[50];
-                       int hdrlen;
-                       write_sha1_file_prepare(target, st.st_size, "blob",
-                                               ce->sha1, hdr, &hdrlen);
-               } else if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
-                       return error("%s: failed to insert into database", path);
-               free(target);
-               break;
-       default:
-               return error("%s: unsupported file type", path);
-       }
+
+       if (index_path(ce->sha1, path, &st, !info_only))
+               return -1;
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
        if (add_cache_entry(ce, option))
@@ -299,6 +272,54 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
                die("Unable to process file %s", path);
 }
 
+static void read_index_info(int line_termination)
+{
+       struct strbuf buf;
+       strbuf_init(&buf);
+       while (1) {
+               char *ptr;
+               unsigned char sha1[20];
+               unsigned int mode;
+
+               read_line(&buf, stdin, line_termination);
+               if (buf.eof)
+                       break;
+
+               mode = strtoul(buf.buf, &ptr, 8);
+               if (ptr == buf.buf || *ptr != ' ' ||
+                   get_sha1_hex(ptr + 1, sha1) ||
+                   ptr[41] != '\t')
+                       goto bad_line;
+
+               ptr += 42;
+               if (!verify_path(ptr)) {
+                       fprintf(stderr, "Ignoring path %s\n", ptr);
+                       continue;
+               }
+
+               if (!mode) {
+                       /* mode == 0 means there is no such path -- remove */
+                       if (remove_file_from_cache(ptr))
+                               die("git-update-index: unable to remove %s",
+                                   ptr);
+               }
+               else {
+                       /* mode ' ' sha1 '\t' name
+                        * ptr[-1] points at tab,
+                        * ptr[-41] is at the beginning of sha1
+                        */
+                       ptr[-42] = ptr[-1] = 0;
+                       if (add_cacheinfo(buf.buf, ptr-41, ptr))
+                               die("git-update-index: unable to update %s",
+                                   ptr);
+               }
+               continue;
+
+       bad_line:
+               die("malformed index info %s", buf.buf);
+       }
+}
+
 int main(int argc, const char **argv)
 {
        int i, newfd, entries, has_errors = 0, line_termination = '\n';
@@ -373,6 +394,11 @@ int main(int argc, const char **argv)
                                read_from_stdin = 1;
                                break;
                        }
+                       if (!strcmp(path, "--index-info")) {
+                               allow_add = allow_replace = allow_remove = 1;
+                               read_index_info(line_termination);
+                               continue;
+                       }
                        if (!strcmp(path, "--ignore-missing")) {
                                not_new = 1;
                                continue;