Merge branch 'master' into ph/strbuf
authorJunio C Hamano <gitster@pobox.com>
Mon, 10 Sep 2007 18:32:58 +0000 (11:32 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 10 Sep 2007 18:32:58 +0000 (11:32 -0700)
* master:
archive - leakfix for format_subst()
Make --no-thin the default in git-push to save server resources
fix doc for --compression argument to pack-objects
git-tag -s must fail if gpg cannot sign the tag.
git-svn: understand grafts when doing dcommit
git-diff: don't squelch the new SHA1 in submodule diffs
Define NO_MEMMEM on Darwin as it lacks the function
git-svn: fix "Malformed network data" with svn:// servers
(cvs|svn)import: Ask git-tag to overwrite old tags.
git-rebase: fix -C option
git-rebase: support --whitespace=<option>
Documentation / grammer nit
archive: rename attribute specfile to export-subst
archive: specfile syntax change: "$Format:%PLCHLDR$" instead of just "%PLCHLDR" (take 2)
add memmem()
Remove unused function convert_sha1_file()
archive: specfile support (--pretty=format: in archive files)
Export format_commit_message()

25 files changed:
Documentation/git-clone.txt
Documentation/git-pack-objects.txt
Documentation/git-rebase.txt
Documentation/gitattributes.txt
Makefile
archive-tar.c
archive-zip.c
archive.h
builtin-archive.c
builtin-push.c
builtin-tag.c
cache.h
commit.c
commit.h
compat/memmem.c [new file with mode: 0644]
convert.c
diff.c
git-compat-util.h
git-cvsimport.perl
git-rebase.sh
git-svn.perl
git-svnimport.perl
t/t5000-tar-tree.sh
t/t7004-tag.sh
t/t7400-submodule-basic.sh
index 227f092e268497aad9c16cff4d796dabf60401de..253f4f03c532eb34912f1b5f570f3f6359692fb9 100644 (file)
@@ -68,7 +68,7 @@ OPTIONS
        automatically setup .git/objects/info/alternates to
        obtain objects from the reference repository.  Using
        an already existing repository as an alternate will
-       require less objects to be copied from the repository
+       require fewer objects to be copied from the repository
        being cloned, reducing network and local storage costs.
 
 --quiet::
index 6f17cff24ab784ee68767724dff6892b67b3ad53..f8a0be351188f39e94c9ffa4dc9a1fcea367e290 100644 (file)
@@ -155,12 +155,8 @@ base-name::
        generated pack.  If not specified,  pack compression level is
        determined first by pack.compression,  then by core.compression,
        and defaults to -1,  the zlib default,  if neither is set.
-       Data copied from loose objects will be recompressed
-       if core.legacyheaders was true when they were created or if
-       the loose compression level (see core.loosecompression and
-       core.compression) is now a different value than the pack
-       compression level.  Add --no-reuse-object if you want to force
-       a uniform compression level on all data no matter the source.
+       Add \--no-reuse-object if you want to force a uniform compression
+       level on all data no matter the source.
 
 --delta-base-offset::
        A packed archive can express base object of a delta as
index 61b1810dbaa56bdc43364ea10d9a5ea0d532f1be..0858fa8a6326a0c457bc9b4bb63ae35d3001ef65 100644 (file)
@@ -8,8 +8,9 @@ git-rebase - Forward-port local commits to the updated upstream head
 SYNOPSIS
 --------
 [verse]
-'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-C<n>]
-       [-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
+       [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
+       [--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
 
 DESCRIPTION
@@ -209,6 +210,10 @@ OPTIONS
        context exist they all must match.  By default no context is
        ever ignored.
 
+--whitespace=<nowarn|warn|error|error-all|strip>::
+       This flag is passed to the `git-apply` program
+       (see gitlink:git-apply[1]) that applies the patch.
+
 -i, \--interactive::
        Make a list of the commits which are about to be rebased.  Let the
        user edit that list before rebasing.  This mode can also be used to
index 46f9d591aa7a3af2235d29308d101b2bfa0bff56..d0e951ee6f1c391459f822b8ecbb4c1237b209db 100644 (file)
@@ -421,6 +421,23 @@ frotz      unspecified
 ----------------------------------------------------------------
 
 
+Creating an archive
+~~~~~~~~~~~~~~~~~~~
+
+`export-subst`
+^^^^^^^^^^^^^^
+
+If the attribute `export-subst` is set for a file then git will expand
+several placeholders when adding this file to an archive.  The
+expansion depends on the availability of a commit ID, i.e. if
+gitlink:git-archive[1] has been given a tree instead of a commit or a
+tag then no replacement will be done.  The placeholders are the same
+as those for the option `--pretty=format:` of gitlink:git-log[1],
+except that they need to be wrapped like this: `$Format:PLACEHOLDERS$`
+in the file.  E.g. the string `$Format:%H$` will be replaced by the
+commit hash.
+
+
 GIT
 ---
 Part of the gitlink:git[7] suite
index dace2112f903f6e5be8220deb3b1502496876bb1..61053aed5633e7cf7fbee739593bfaed2a1b78ce 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,8 @@ all::
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
+# Define NO_MEMMEM if you don't have memmem.
+#
 # Define NO_STRLCPY if you don't have strlcpy.
 #
 # Define NO_STRTOUMAX if you don't have strtoumax in the C library.
@@ -396,12 +398,14 @@ ifeq ($(uname_S),Darwin)
        NEEDS_LIBICONV = YesPlease
        OLD_ICONV = UnfortunatelyYes
        NO_STRLCPY = YesPlease
+       NO_MEMMEM = YesPlease
 endif
 ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
        NEEDS_NSL = YesPlease
        SHELL_PATH = /bin/bash
        NO_STRCASESTR = YesPlease
+       NO_MEMMEM = YesPlease
        NO_HSTRERROR = YesPlease
        ifeq ($(uname_R),5.8)
                NEEDS_LIBICONV = YesPlease
@@ -424,6 +428,7 @@ ifeq ($(uname_O),Cygwin)
        NO_D_TYPE_IN_DIRENT = YesPlease
        NO_D_INO_IN_DIRENT = YesPlease
        NO_STRCASESTR = YesPlease
+       NO_MEMMEM = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
@@ -442,6 +447,7 @@ ifeq ($(uname_S),FreeBSD)
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
+       NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@ -456,6 +462,7 @@ ifeq ($(uname_S),NetBSD)
 endif
 ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
+       NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NEEDS_LIBICONV=YesPlease
 endif
@@ -467,6 +474,7 @@ ifeq ($(uname_S),IRIX64)
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
+       NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NO_SOCKADDR_STORAGE=YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
@@ -661,6 +669,10 @@ ifdef NO_HSTRERROR
        COMPAT_CFLAGS += -DNO_HSTRERROR
        COMPAT_OBJS += compat/hstrerror.o
 endif
+ifdef NO_MEMMEM
+       COMPAT_CFLAGS += -DNO_MEMMEM
+       COMPAT_OBJS += compat/memmem.o
+endif
 
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
index c84d7c0652783bfd039fe66e4ce2ca59f6a7df8f..0612bb6051e1c60d5151b0c65d097fe51ba93f2e 100644 (file)
@@ -17,6 +17,7 @@ static unsigned long offset;
 static time_t archive_time;
 static int tar_umask = 002;
 static int verbose;
+static const struct commit *commit;
 
 /* writes out the whole block, but only if it is full */
 static void write_if_needed(void)
@@ -252,7 +253,8 @@ static int write_tar_entry(const unsigned char *sha1,
                buffer = NULL;
                size = 0;
        } else {
-               buffer = convert_sha1_file(path.buf, sha1, mode, &type, &size);
+               buffer = sha1_file_to_archive(path.buf, sha1, mode, &type,
+                                             &size, commit);
                if (!buffer)
                        die("cannot read %s", sha1_to_hex(sha1));
        }
@@ -271,6 +273,7 @@ int write_tar_archive(struct archiver_args *args)
 
        archive_time = args->time;
        verbose = args->verbose;
+       commit = args->commit;
 
        if (args->commit_sha1)
                write_global_extended_header(args->commit_sha1);
index 444e1623db66fe4f5d8983e1e9e2cf4083b005b4..f63dff383483c482b414432b697306c53b320d00 100644 (file)
@@ -12,6 +12,7 @@
 static int verbose;
 static int zip_date;
 static int zip_time;
+static const struct commit *commit;
 
 static unsigned char *zip_dir;
 static unsigned int zip_dir_size;
@@ -195,7 +196,8 @@ static int write_zip_entry(const unsigned char *sha1,
                if (S_ISREG(mode) && zlib_compression_level != 0)
                        method = 8;
                result = 0;
-               buffer = convert_sha1_file(path, sha1, mode, &type, &size);
+               buffer = sha1_file_to_archive(path, sha1, mode, &type, &size,
+                                             commit);
                if (!buffer)
                        die("cannot read %s", sha1_to_hex(sha1));
                crc = crc32(crc, buffer, size);
@@ -316,6 +318,7 @@ int write_zip_archive(struct archiver_args *args)
        zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
        zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
        verbose = args->verbose;
+       commit = args->commit;
 
        if (args->base && plen > 0 && args->base[plen - 1] == '/') {
                char *base = xstrdup(args->base);
index 6838dc788f7620b0807a7044b611efc623bdcf0c..5791e657e9a0c22081f4f42b9d8ca5b3c536baf2 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -8,6 +8,7 @@ struct archiver_args {
        const char *base;
        struct tree *tree;
        const unsigned char *commit_sha1;
+       const struct commit *commit;
        time_t time;
        const char **pathspec;
        unsigned int verbose : 1;
@@ -42,4 +43,6 @@ extern int write_tar_archive(struct archiver_args *);
 extern int write_zip_archive(struct archiver_args *);
 extern void *parse_extra_zip_args(int argc, const char **argv);
 
+extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit);
+
 #endif /* ARCHIVE_H */
index 187491bc172571b783a0be4f4dfa3d94d58bb0fe..a90c65ce54a1c3e415b46465a2b4c7bfdffca9ff 100644 (file)
@@ -10,6 +10,7 @@
 #include "exec_cmd.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "attr.h"
 
 static const char archive_usage[] = \
 "git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
@@ -80,6 +81,100 @@ static int run_remote_archiver(const char *remote, int argc,
        return !!rv;
 }
 
+static void *format_subst(const struct commit *commit, const char *format,
+                          unsigned long *sizep)
+{
+       unsigned long len = *sizep, result_len = 0;
+       const char *a = format;
+       char *result = NULL;
+
+       for (;;) {
+               const char *b, *c;
+               char *fmt, *formatted = NULL;
+               unsigned long a_len, fmt_len, formatted_len, allocated = 0;
+
+               b = memmem(a, len, "$Format:", 8);
+               if (!b || a + len < b + 9)
+                       break;
+               c = memchr(b + 8, '$', len - 8);
+               if (!c)
+                       break;
+
+               a_len = b - a;
+               fmt_len = c - b - 8;
+               fmt = xmalloc(fmt_len + 1);
+               memcpy(fmt, b + 8, fmt_len);
+               fmt[fmt_len] = '\0';
+
+               formatted_len = format_commit_message(commit, fmt, &formatted,
+                                                     &allocated);
+               free(fmt);
+               result = xrealloc(result, result_len + a_len + formatted_len);
+               memcpy(result + result_len, a, a_len);
+               memcpy(result + result_len + a_len, formatted, formatted_len);
+               result_len += a_len + formatted_len;
+               len -= c + 1 - a;
+               a = c + 1;
+       }
+
+       if (result && len) {
+               result = xrealloc(result, result_len + len);
+               memcpy(result + result_len, a, len);
+               result_len += len;
+       }
+
+       *sizep = result_len;
+
+       return result;
+}
+
+static void *convert_to_archive(const char *path,
+                                const void *src, unsigned long *sizep,
+                                const struct commit *commit)
+{
+       static struct git_attr *attr_export_subst;
+       struct git_attr_check check[1];
+
+       if (!commit)
+               return NULL;
+
+        if (!attr_export_subst)
+                attr_export_subst = git_attr("export-subst", 12);
+
+       check[0].attr = attr_export_subst;
+       if (git_checkattr(path, ARRAY_SIZE(check), check))
+               return NULL;
+       if (!ATTR_TRUE(check[0].value))
+               return NULL;
+
+       return format_subst(commit, src, sizep);
+}
+
+void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
+                           unsigned int mode, enum object_type *type,
+                           unsigned long *size,
+                           const struct commit *commit)
+{
+       void *buffer, *converted;
+
+       buffer = read_sha1_file(sha1, type, size);
+       if (buffer && S_ISREG(mode)) {
+               converted = convert_to_working_tree(path, buffer, size);
+               if (converted) {
+                       free(buffer);
+                       buffer = converted;
+               }
+
+               converted = convert_to_archive(path, buffer, size, commit);
+               if (converted) {
+                       free(buffer);
+                       buffer = converted;
+               }
+       }
+
+       return buffer;
+}
+
 static int init_archiver(const char *name, struct archiver *ar)
 {
        int rv = -1, i;
@@ -109,7 +204,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
        const unsigned char *commit_sha1;
        time_t archive_time;
        struct tree *tree;
-       struct commit *commit;
+       const struct commit *commit;
        unsigned char sha1[20];
 
        if (get_sha1(name, sha1))
@@ -142,6 +237,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
        }
        ar_args->tree = tree;
        ar_args->commit_sha1 = commit_sha1;
+       ar_args->commit = commit;
        ar_args->time = archive_time;
 }
 
index 2612f07f74855ad6dec8ccd605279ab3a502e5e2..88c5024da7c9831e69ee20ca20ed9bdb5ddee63f 100644 (file)
@@ -9,7 +9,7 @@
 
 static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
 
-static int all, force, thin = 1, verbose;
+static int all, force, thin, verbose;
 static const char *receivepack;
 
 static const char **refspec;
index 348919cff8645c81ac803a34c967ba34f9b982b4..3a9d2eea71434c532bd0fe572bc799c0b91f4f44 100644 (file)
@@ -200,6 +200,10 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
                        bracket[1] = '\0';
        }
 
+       /* When the username signingkey is bad, program could be terminated
+        * because gpg exits without reading and then write gets SIGPIPE. */
+       signal(SIGPIPE, SIG_IGN);
+
        memset(&gpg, 0, sizeof(gpg));
        gpg.argv = args;
        gpg.in = -1;
@@ -212,12 +216,17 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
        if (start_command(&gpg))
                return error("could not run gpg.");
 
-       write_or_die(gpg.in, buffer, size);
+       if (write_in_full(gpg.in, buffer, size) != size) {
+               close(gpg.in);
+               finish_command(&gpg);
+               return error("gpg did not accept the tag data");
+       }
        close(gpg.in);
        gpg.close_in = 0;
        len = read_in_full(gpg.out, buffer + size, max - size);
 
-       finish_command(&gpg);
+       if (finish_command(&gpg) || !len || len < 0)
+               return error("gpg failed to sign the tag");
 
        if (len == max - size)
                return error("could not read the entire signature from gpg.");
@@ -310,9 +319,10 @@ static void create_tag(const unsigned char *object, const char *tag,
        size += header_len;
 
        if (sign) {
-               size = do_sign(buffer, size, max_size);
-               if (size < 0)
+               ssize_t r = do_sign(buffer, size, max_size);
+               if (r < 0)
                        die("unable to sign the tag");
+               size = r;
        }
 
        if (write_sha1_file(buffer, size, tag_type, result) < 0)
diff --git a/cache.h b/cache.h
index 70abbd59bf8d3ca118836605099b0b5dc66a27b7..493983cbaef90cb845d0660dce19072b6ec0b353 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -592,7 +592,6 @@ extern void trace_argv_printf(const char **argv, int count, const char *format,
 /* convert.c */
 extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep);
 extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep);
-extern void *convert_sha1_file(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
index dc5a0643f3d52797f29706595355fca824d9feda..99f65cee0e7e30e833f850b5993aa8a5797ba889 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -787,8 +787,8 @@ static void fill_person(struct interp *table, const char *msg, int len)
        interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
 }
 
-static long format_commit_message(const struct commit *commit,
-               const char *msg, char **buf_p, unsigned long *space_p)
+long format_commit_message(const struct commit *commit, const void *format,
+                           char **buf_p, unsigned long *space_p)
 {
        struct interp table[] = {
                { "%H" },       /* commit hash */
@@ -843,6 +843,7 @@ static long format_commit_message(const struct commit *commit,
        char parents[1024];
        int i;
        enum { HEADER, SUBJECT, BODY } state;
+       const char *msg = commit->buffer;
 
        if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
                die("invalid interp table!");
@@ -924,7 +925,7 @@ static long format_commit_message(const struct commit *commit,
                char *buf = *buf_p;
                unsigned long space = *space_p;
 
-               space = interpolate(buf, space, user_format,
+               space = interpolate(buf, space, format,
                                    table, ARRAY_SIZE(table));
                if (!space)
                        break;
@@ -1165,7 +1166,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
        char *buf;
 
        if (fmt == CMIT_FMT_USERFORMAT)
-               return format_commit_message(commit, msg, buf_p, space_p);
+               return format_commit_message(commit, user_format, buf_p, space_p);
 
        encoding = (git_log_output_encoding
                    ? git_log_output_encoding
index 467872eecabf05ccedbb9bf8247b6de244416b8f..a8d76616d2ae6965ebca3c197232e1b7c9294585 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -61,6 +61,7 @@ enum cmit_fmt {
 };
 
 extern enum cmit_fmt get_commit_format(const char *arg);
+extern long format_commit_message(const struct commit *commit, const void *template, char **buf_p, unsigned long *space_p);
 extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
 
 /** Removes the first commit from a list sorted by date, and adds all
diff --git a/compat/memmem.c b/compat/memmem.c
new file mode 100644 (file)
index 0000000..cd0d877
--- /dev/null
@@ -0,0 +1,29 @@
+#include "../git-compat-util.h"
+
+void *gitmemmem(const void *haystack, size_t haystack_len,
+                const void *needle, size_t needle_len)
+{
+       const char *begin = haystack;
+       const char *last_possible = begin + haystack_len - needle_len;
+
+       /*
+        * The first occurrence of the empty string is deemed to occur at
+        * the beginning of the string.
+        */
+       if (needle_len == 0)
+               return (void *)begin;
+
+       /*
+        * Sanity check, otherwise the loop might search through the whole
+        * memory.
+        */
+       if (haystack_len < needle_len)
+               return NULL;
+
+       for (; begin <= last_possible; begin++) {
+               if (!memcmp(begin, needle, needle_len))
+                       return (void *)begin;
+       }
+
+       return NULL;
+}
index 21908b10398492c0b07f705ed3b1ce7a06ac6b44..d77c8eb8b2802d675b320ddeb063c1cf70cc57d8 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -687,18 +687,3 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long *
 
        return buf;
 }
-
-void *convert_sha1_file(const char *path, const unsigned char *sha1,
-                        unsigned int mode, enum object_type *type,
-                        unsigned long *size)
-{
-       void *buffer = read_sha1_file(sha1, type, size);
-       if (S_ISREG(mode) && buffer) {
-               void *converted = convert_to_working_tree(path, buffer, size);
-               if (converted) {
-                       free(buffer);
-                       buffer = converted;
-               }
-       }
-       return buffer;
-}
diff --git a/diff.c b/diff.c
index c054b234b877952501dcf05b2ea312ae77441042..26d7bb96bcaa52b4a104a2dfa17da388e3032c49 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3135,6 +3135,22 @@ static void diffcore_apply_filter(const char *filter)
        *q = outq;
 }
 
+/* Check whether two filespecs with the same mode and size are identical */
+static int diff_filespec_is_identical(struct diff_filespec *one,
+                                     struct diff_filespec *two)
+{
+       if (S_ISGITLINK(one->mode)) {
+               diff_fill_sha1_info(one);
+               diff_fill_sha1_info(two);
+               return !hashcmp(one->sha1, two->sha1);
+       }
+       if (diff_populate_filespec(one, 0))
+               return 0;
+       if (diff_populate_filespec(two, 0))
+               return 0;
+       return !memcmp(one->data, two->data, one->size);
+}
+
 static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
 {
        int i;
@@ -3166,10 +3182,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
                    diff_populate_filespec(p->one, 1) ||
                    diff_populate_filespec(p->two, 1) ||
                    (p->one->size != p->two->size) ||
-
-                   diff_populate_filespec(p->one, 0) || /* (2) */
-                   diff_populate_filespec(p->two, 0) ||
-                   memcmp(p->one->data, p->two->data, p->one->size))
+                   !diff_filespec_is_identical(p->one, p->two)) /* (2) */
                        diff_q(&outq, p);
                else {
                        /*
index ca0a597a282328bff9e0e29fd3653dbd656489fb..1bfbdeb94f55d57b429b91aa8762618153c34f7f 100644 (file)
@@ -172,6 +172,12 @@ extern uintmax_t gitstrtoumax(const char *, char **, int);
 extern const char *githstrerror(int herror);
 #endif
 
+#ifdef NO_MEMMEM
+#define memmem gitmemmem
+void *gitmemmem(const void *haystack, size_t haystacklen,
+                const void *needle, size_t needlelen);
+#endif
+
 extern void release_pack_memory(size_t, int);
 
 static inline char* xstrdup(const char *str)
index ba23eb8eebdb841139062d4221c87515b96d8729..2954fb846e3372a648124d92b7c1a1ccbd44306f 100755 (executable)
@@ -779,7 +779,7 @@ sub commit {
                $xtag =~ tr/_/\./ if ( $opt_u );
                $xtag =~ s/[\/]/$opt_s/g;
 
-               system('git-tag', $xtag, $cid) == 0
+               system('git-tag', '-f', $xtag, $cid) == 0
                        or die "Cannot create tag $xtag: $!\n";
 
                print "Created tag '$xtag' on '$branch'\n" if $opt_v;
index 3bd66b0a04bac05a3447d68112d4d933480eaf04..c9942f24009bd01ee0fee8990f64e64d9e43364c 100755 (executable)
@@ -216,9 +216,11 @@ do
        -v|--verbose)
                verbose=t
                ;;
+       --whitespace=*)
+               git_am_opt="$git_am_opt $1"
+               ;;
        -C*)
-               git_am_opt=$1
-               shift
+               git_am_opt="$git_am_opt $1"
                ;;
        -*)
                usage
index d3c8cd0b8e3cfb97c6deb2453097c6f2e16e3bcf..f8181609f942b22dc95992740326e8b5788feba5 100755 (executable)
@@ -841,14 +841,9 @@ sub working_head_info {
 
 sub read_commit_parents {
        my ($parents, $c) = @_;
-       my ($fh, $ctx) = command_output_pipe(qw/cat-file commit/, $c);
-       while (<$fh>) {
-               chomp;
-               last if '';
-               /^parent ($sha1)/ or next;
-               push @{$parents->{$c}}, $1;
-       }
-       close $fh; # break the pipe
+       chomp(my $p = command_oneline(qw/rev-list --parents -1/, $c));
+       $p =~ s/^($c)\s*// or die "rev-list --parents -1 $c failed!\n";
+       @{$parents->{$c}} = split(/ /, $p);
 }
 
 sub linearize_history {
@@ -3013,7 +3008,7 @@ package Git::SVN::Ra;
 use vars qw/@ISA $config_dir $_log_window_size/;
 use strict;
 use warnings;
-my ($can_do_switch, %ignored_err, $RA);
+my ($ra_invalid, $can_do_switch, %ignored_err, $RA);
 
 BEGIN {
        # enforce temporary pool usage for some simple functions
@@ -3174,7 +3169,11 @@ sub gs_do_switch {
                        $self->{url} = $full_url;
                        $reparented = 1;
                } else {
+                       $_[0] = undef;
+                       $self = undef;
+                       $RA = undef;
                        $ra = Git::SVN::Ra->new($full_url);
+                       $ra_invalid = 1;
                }
        }
        $ra ||= $self;
@@ -3234,6 +3233,7 @@ sub gs_fetch_loop_common {
        my $inc = $_log_window_size;
        my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
        my $longest_path = longest_common_path($gsv, $globs);
+       my $ra_url = $self->{url};
        while (1) {
                my %revs;
                my $err;
@@ -3295,6 +3295,13 @@ sub gs_fetch_loop_common {
                                        "$g->{t}-maxRev";
                                Git::SVN::tmp_config($k, $r);
                        }
+                       if ($ra_invalid) {
+                               $_[0] = undef;
+                               $self = undef;
+                               $RA = undef;
+                               $self = Git::SVN::Ra->new($ra_url);
+                               $ra_invalid = undef;
+                       }
                }
                # pre-fill the .rev_db since it'll eventually get filled in
                # with '0' x40 if something new gets committed
index 8c17fb5ae240c0335ce211b19d516f54e145065f..d3ad5b904f3d025aaf89aa5a7ef2e6464fd9b5b8 100755 (executable)
@@ -873,7 +873,7 @@ sub commit {
 
                $dest =~ tr/_/\./ if $opt_u;
 
-               system('git-tag', $dest, $cid) == 0
+               system('git-tag', '-f', $dest, $cid) == 0
                        or die "Cannot create tag $dest: $!\n";
 
                print "Created tag '$dest' on '$branch'\n" if $opt_v;
index 1a4c53a031608a16785e6ac9a0531696156bacba..42e28ab758358500183ee8ade35d7052c69ce9e1 100755 (executable)
@@ -28,12 +28,15 @@ commit id embedding:
 TAR=${TAR:-tar}
 UNZIP=${UNZIP:-unzip}
 
+SUBSTFORMAT=%H%n
+
 test_expect_success \
     'populate workdir' \
     'mkdir a b c &&
      echo simple textfile >a/a &&
      mkdir a/bin &&
      cp /bin/sh a/bin &&
+     printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile &&
      ln -s a a/l1 &&
      (p=long_path_to_a_file && cd a &&
       for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
@@ -104,6 +107,22 @@ test_expect_success \
     'validate file contents with prefix' \
     'diff -r a c/prefix/a'
 
+test_expect_success \
+    'create an archive with a substfile' \
+    'echo substfile export-subst >a/.gitattributes &&
+     git archive HEAD >f.tar &&
+     rm a/.gitattributes'
+
+test_expect_success \
+    'extract substfile' \
+    '(mkdir f && cd f && $TAR xf -) <f.tar'
+
+test_expect_success \
+     'validate substfile contents' \
+     'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+      >f/a/substfile.expected &&
+      diff f/a/substfile.expected f/a/substfile'
+
 test_expect_success \
     'git archive --format=zip' \
     'git archive --format=zip HEAD >d.zip'
index 606d4f2a2c3e771c188294ae92d58cf2a38e14fb..0d07bc39c745ade65370dde35f43f16a37231179 100755 (executable)
@@ -990,6 +990,13 @@ test_expect_success \
        git diff expect actual
 '
 
+# try to sign with bad user.signingkey
+git config user.signingkey BobTheMouse
+test_expect_failure \
+       'git-tag -s fails if gpg is misconfigured' \
+       'git tag -s -m tail tag-gpg-failure'
+git config --unset user.signingkey
+
 # try to verify without gpg:
 
 rm -rf gpghome
index 9d142ed649ef98adcbf7c6d158890c9f55370ef7..4fe3a41f07f2f05865b219ff00afaa5786c679d5 100755 (executable)
@@ -152,6 +152,10 @@ test_expect_success 'the --cached sha1 should be rev1' '
        git-submodule --cached status | grep "^+$rev1"
 '
 
+test_expect_success 'git diff should report the SHA1 of the new submodule commit' '
+       git-diff | grep "^+Subproject commit $rev2"
+'
+
 test_expect_success 'update should checkout rev1' '
        git-submodule update &&
        head=$(cd lib && git rev-parse HEAD) &&