]> asedeno.scripts.mit.edu Git - git.git/commitdiff
Merge branch 'jc/bindiff' into next
authorJunio C Hamano <junkio@cox.net>
Fri, 5 May 2006 22:36:04 +0000 (15:36 -0700)
committerJunio C Hamano <junkio@cox.net>
Fri, 5 May 2006 22:36:04 +0000 (15:36 -0700)
* jc/bindiff:
  binary diff: further updates.
  binary patch.
  pack-object: squelch eye-candy on non-tty
  core.prefersymlinkrefs: use symlinks for .git/HEAD
  repo-config: trim white-space before comment
  Fix for config file section parsing.
  Clarify git-cherry documentation.
  Update git-unpack-objects documentation.
  Fix up docs where "--" isn't displayed correctly.
  Several trivial documentation touch ups.
  git-svn 1.0.0
  git-svn: documentation updates
  delta: stricter constness
  Makefile: do not link rev-list any specially.
  builtin-push: --all and --tags _are_ explicit refspecs

31 files changed:
Documentation/git-add.txt
Documentation/git-checkout-index.txt
Documentation/git-cherry.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-imap-send.txt
Documentation/git-log.txt
Documentation/git-ls-files.txt
Documentation/git-merge-index.txt
Documentation/git-name-rev.txt
Documentation/git-prune.txt
Documentation/git-repack.txt
Documentation/git-repo-config.txt
Documentation/git-reset.txt
Documentation/git-rm.txt
Documentation/git-unpack-objects.txt
Documentation/git-update-index.txt
Documentation/git-verify-pack.txt
Documentation/git-whatchanged.txt
Documentation/gitk.txt
Makefile
apply.c
base85.c [new file with mode: 0644]
builtin-push.c
cache.h
config.c
contrib/git-svn/git-svn.perl
contrib/git-svn/git-svn.txt
diff.c
diff.h
pack-objects.c

index ae24547c8a03f5a2b9b850062c6237b35cff70c7..5e3112943d1947dbd8b58c23dd535eb4d527b80f 100644 (file)
@@ -26,7 +26,7 @@ OPTIONS
 -v::
         Be verbose.
 
---::
+\--::
        This option can be used to separate command-line options from
        the list of files, (useful when filenames might be mistaken
        for command-line options).
index 09bd6a5535835b50da0848892d1fb497710d9781..765c173e1507056953e61159f32b9af6b64a3b15 100644 (file)
@@ -63,7 +63,7 @@ OPTIONS
        Only meaningful with `--stdin`; paths are separated with
        NUL character instead of LF.
 
---::
+\--::
        Do not interpret any more arguments as options.
 
 The order of the flags used to matter, but not anymore.
index 9a5e37186fcd03972d490e2e996ce8b078112bbb..893baaa6f656616b2151c572450e037fabee7622 100644 (file)
@@ -11,11 +11,20 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Each commit between the fork-point and <head> is examined, and compared against
-the change each commit between the fork-point and <upstream> introduces.
-Commits already included in upstream are prefixed with '-' (meaning "drop from
-my local pull"), while commits missing from upstream are prefixed with '+'
-(meaning "add to the updated upstream").
+The changeset (or "diff") of each commit between the fork-point and <head>
+is compared against each commit between the fork-point and <upstream>.
+
+Every commit with a changeset that doesn't exist in the other branch
+has its id (sha1) reported, prefixed by a symbol.  Those existing only
+in the <upstream> branch are prefixed with a minus (-) sign, and those
+that only exist in the <head> branch are prefixed with a plus (+) symbol.
+
+Because git-cherry compares the changeset rather than the commit id
+(sha1), you can use git-cherry to find out if a commit you made locally
+has been applied <upstream> under a different commit id.  For example,
+this will happen if you're feeding patches <upstream> via email rather
+than pushing or pulling commits directly.
+
 
 OPTIONS
 -------
index 131e445747db6f9d573207ba5395b0264f983efd..b333f510454fff9dc6828b62ca8a0b0dc4c116f6 100644 (file)
@@ -101,7 +101,7 @@ OPTIONS
        is not allowed.
 
 Examples
-~~~~~~~~
+--------
 
 Clone from upstream::
 +
index 0a7365b9a80ed0d134bccb3fbe4baacc59056423..38df59ce2357fcd37810b2aaca9d759e9138e654 100644 (file)
@@ -106,7 +106,7 @@ but can be used to amend a merge commit.
        index and the latest commit does not match on the
        specified paths to avoid confusion.
 
---::
+\--::
        Do not interpret any more arguments as options.
 
 <file>...::
index cfc0d88d027dbf84a0e754387a9611076dc74489..eca9e9ccef49e667fffcae9238866123911a24c8 100644 (file)
@@ -29,6 +29,7 @@ CONFIGURATION
 git-imap-send requires the following values in the repository
 configuration file (shown with examples):
 
+..........................
 [imap]
     Folder = "INBOX.Drafts"
 
@@ -38,8 +39,9 @@ configuration file (shown with examples):
 [imap]
     Host = imap.server.com
     User = bob
-    Password = pwd
+    Pass = pwd
     Port = 143
+..........................
 
 
 BUGS
index af378ffcf9b09bd3ae01566e405aadec75c28e61..c9ffff734c256d7db1470381f0eb74a8d7f81873 100644 (file)
@@ -51,7 +51,7 @@ git log v2.6.12.. include/scsi drivers/scsi::
        Show all commits since version 'v2.6.12' that changed any file
        in the include/scsi or drivers/scsi subdirectories
 
-git log --since="2 weeks ago" -- gitk::
+git log --since="2 weeks ago" \-- gitk::
 
        Show the changes during the last two weeks to the file 'gitk'.
        The "--" is necessary to avoid confusion with the *branch* named
index 796d049be69469b0ce0be61e9f5c88c19911d655..a29c633c8dfd15ee4bd3c8f4ae7aeceef91ea7fe 100644 (file)
@@ -106,7 +106,7 @@ OPTIONS
        lines, show only handful hexdigits prefix.
        Non default number of digits can be specified with --abbrev=<n>.
 
---::
+\--::
        Do not interpret any more arguments as options.
 
 <file>::
index fbc986aa84d180d304a35a94dcd1f80e6a848b80..332e023d0f14be813ead6cb6915938910ea3a719 100644 (file)
@@ -8,7 +8,7 @@ git-merge-index - Runs a merge for files needing merging
 
 SYNOPSIS
 --------
-'git-merge-index' [-o] [-q] <merge-program> (-a | -- | <file>\*)
+'git-merge-index' [-o] [-q] <merge-program> (-a | \-- | <file>\*)
 
 DESCRIPTION
 -----------
@@ -19,7 +19,7 @@ files are passed as arguments 5, 6 and 7.
 
 OPTIONS
 -------
---::
+\--::
        Do not interpret any more arguments as options.
 
 -a::
index 68707083bebc24e2773878c997059fd642c96820..ffaa00468f0c16f19ab632aac20aaa74db52b325 100644 (file)
@@ -41,6 +41,7 @@ Enter git-name-rev:
 
 ------------
 % git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a
+33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99^0~940
 ------------
 
 Now you are wiser, because you know that it happened 940 revisions before v0.99.
index f694fcbde809b17dc9fd974eba458dd7805dc426..a11e3030943ab3ffd10afa017640826795809d05 100644 (file)
@@ -28,7 +28,7 @@ OPTIONS
        Do not remove anything; just report what it would
        remove.
 
---::
+\--::
        Do not interpret any more arguments as options.
 
 <head>...::
index d2f9a44382f3e74860bb5f489acf2843281ffb1c..951622774af70e92590740c27fe021223f29442a 100644 (file)
@@ -38,6 +38,7 @@ OPTIONS
 -d::
        After packing, if the newly created packs make some
        existing packs redundant, remove the redundant packs.
+       Also runs gitlink:git-prune-packed[1].
 
 -l::
         Pass the `--local` option to `git pack-objects`, see
index ddcf52364ca0d096eef99b11621d5e7f819502bd..fd44f629f5febb49a647578adb7804cd34b3afd6 100644 (file)
@@ -34,10 +34,10 @@ convert the value to the canonical form (simple decimal number for int,
 a "true" or "false" string for bool). If no type specifier is passed,
 no checks or transformations are performed on the value.
 
-This command will fail if
+This command will fail if:
 
-. .git/config is invalid,
-. .git/config can not be written to,
+. The .git/config file is invalid,
+. Can not write to .git/config,
 . no section was provided,
 . the section or key is invalid,
 . you try to unset an option which does not exist, or
index ebcfe5edb73506df698b594f115bc9390f1dae1f..b27399dd419d3b673e230fd3aad21a8e5c1b2e09 100644 (file)
@@ -43,7 +43,7 @@ OPTIONS
        Commit to make the current HEAD.
 
 Examples
-~~~~~~~~
+--------
 
 Undo a commit and redo::
 +
index c9c3088424ebd45e2220efde104e47b6e4cd8316..66fc478f5736957d56de10ddc69c5c0fd9aad9c6 100644 (file)
@@ -32,7 +32,7 @@ OPTIONS
 -v::
         Be verbose.
 
---::
+\--::
        This option can be used to separate command-line options from
        the list of files, (useful when filenames might be mistaken
        for command-line options).
index 18280628a15510d098faf0289fbf61bae7e1d688..c20b38b08adca264316f35b064153213a0bfd7c3 100644 (file)
@@ -13,9 +13,16 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Reads a packed archive (.pack) from the standard input, and
-expands the objects contained in the pack into "one-file
-one-object" format in $GIT_OBJECT_DIRECTORY.
+Read a packed archive (.pack) from the standard input, expanding
+the objects contained within and writing them into the repository in
+"loose" (one object per file) format.
+
+Objects that already exist in the repository will *not* be unpacked
+from the pack-file.  Therefore, nothing will be unpacked if you use
+this command on a pack-file that exists within the target repository.
+
+Please see the `git-repack` documentation for options to generate
+new packs and replace existing ones.
 
 OPTIONS
 -------
index 23f2b6f1a5bd40f9e60efaf70b9c59e230979a15..57177c7fb6fea75a39c3243495b0724a207add17 100644 (file)
@@ -113,7 +113,7 @@ OPTIONS
        Only meaningful with `--stdin`; paths are separated with
        NUL character instead of LF.
 
---::
+\--::
        Do not interpret any more arguments as options.
 
 <file>::
index 4962d6975f03df28f0ab9b733dd6c17c1c38f1cb..7a6132b016903801fd1070e062381a41ec52536a 100644 (file)
@@ -25,7 +25,7 @@ OPTIONS
 -v::
        After verifying the pack, show list of objects contained
        in the pack.
---::
+\--::
        Do not interpret any more arguments as options.
 
 OUTPUT FORMAT
index 641cb7ea97feee2971f59cd6113dc85bd41b4480..e8f21d02f774a914e7e8840150f6db86a2884178 100644 (file)
@@ -58,7 +58,7 @@ git-whatchanged -p v2.6.12.. include/scsi drivers/scsi::
        Show as patches the commits since version 'v2.6.12' that changed
        any file in the include/scsi or drivers/scsi subdirectories
 
-git-whatchanged --since="2 weeks ago" -- gitk::
+git-whatchanged --since="2 weeks ago" \-- gitk::
 
        Show the changes during the last two weeks to the file 'gitk'.
        The "--" is necessary to avoid confusion with the *branch* named
index eb126d7a4b02c979709dff716eb9c9a64c169952..cb482bf98ef9e2509ab2d2e1665933687538a574 100644 (file)
@@ -31,7 +31,7 @@ gitk v2.6.12.. include/scsi drivers/scsi::
        Show as the changes since version 'v2.6.12' that changed any
        file in the include/scsi or drivers/scsi subdirectories
 
-gitk --since="2 weeks ago" -- gitk::
+gitk --since="2 weeks ago" \-- gitk::
 
        Show the changes during the last two weeks to the file 'gitk'.
        The "--" is necessary to avoid confusion with the *branch* named
index e9d9e101d393dd037728fa4552d8b9fc03270557..070c478bf291898060faf02ea86d416b765b39f1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -205,7 +205,7 @@ DIFF_OBJS = \
        diffcore-delta.o log-tree.o
 
 LIB_OBJS = \
-       blob.o commit.o connect.o csum-file.o cache-tree.o \
+       blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
        date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o \
        quote.o read-cache.o refs.o run-command.o \
@@ -565,10 +565,6 @@ git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-git-rev-list$X: rev-list.o $(LIB_FILE)
-       $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-               $(LIBS) $(OPENSSL_LIBSSL)
-
 init-db.o: init-db.c
        $(CC) -c $(ALL_CFLAGS) \
                -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
diff --git a/apply.c b/apply.c
index acecf8de54e0446c16da2291de6d90b4606f3287..88d2a3206b0c46e31d2459f616e0d7bc7fb93a0f 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -11,6 +11,7 @@
 #include "cache-tree.h"
 #include "quote.h"
 #include "blob.h"
+#include "delta.h"
 
 //  --check turns on checking that the working tree matches the
 //    files that are being modified, but doesn't apply the patch
@@ -114,6 +115,9 @@ struct patch {
        char *new_name, *old_name, *def_name;
        unsigned int old_mode, new_mode;
        int is_rename, is_copy, is_new, is_delete, is_binary;
+#define BINARY_DELTA_DEFLATED 1
+#define BINARY_LITERAL_DEFLATED 2
+       unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
        struct fragment *fragments;
@@ -967,6 +971,88 @@ static inline int metadata_changes(struct patch *patch)
                 patch->old_mode != patch->new_mode);
 }
 
+static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+{
+       /* We have read "GIT binary patch\n"; what follows is a line
+        * that says the patch method (currently, either "deflated
+        * literal" or "deflated delta") and the length of data before
+        * deflating; a sequence of 'length-byte' followed by base-85
+        * encoded data follows.
+        *
+        * Each 5-byte sequence of base-85 encodes up to 4 bytes,
+        * and we would limit the patch line to 66 characters,
+        * so one line can fit up to 13 groups that would decode
+        * to 52 bytes max.  The length byte 'A'-'Z' corresponds
+        * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
+        * The end of binary is signalled with an empty line.
+        */
+       int llen, used;
+       struct fragment *fragment;
+       char *data = NULL;
+
+       patch->fragments = fragment = xcalloc(1, sizeof(*fragment));
+
+       /* Grab the type of patch */
+       llen = linelen(buffer, size);
+       used = llen;
+       linenr++;
+
+       if (!strncmp(buffer, "delta ", 6)) {
+               patch->is_binary = BINARY_DELTA_DEFLATED;
+               patch->deflate_origlen = strtoul(buffer + 6, NULL, 10);
+       }
+       else if (!strncmp(buffer, "literal ", 8)) {
+               patch->is_binary = BINARY_LITERAL_DEFLATED;
+               patch->deflate_origlen = strtoul(buffer + 8, NULL, 10);
+       }
+       else
+               return error("unrecognized binary patch at line %d: %.*s",
+                            linenr-1, llen-1, buffer);
+       buffer += llen;
+       while (1) {
+               int byte_length, max_byte_length, newsize;
+               llen = linelen(buffer, size);
+               used += llen;
+               linenr++;
+               if (llen == 1)
+                       break;
+               /* Minimum line is "A00000\n" which is 7-byte long,
+                * and the line length must be multiple of 5 plus 2.
+                */
+               if ((llen < 7) || (llen-2) % 5)
+                       goto corrupt;
+               max_byte_length = (llen - 2) / 5 * 4;
+               byte_length = *buffer;
+               if ('A' <= byte_length && byte_length <= 'Z')
+                       byte_length = byte_length - 'A' + 1;
+               else if ('a' <= byte_length && byte_length <= 'z')
+                       byte_length = byte_length - 'a' + 27;
+               else
+                       goto corrupt;
+               /* if the input length was not multiple of 4, we would
+                * have filler at the end but the filler should never
+                * exceed 3 bytes
+                */
+               if (max_byte_length < byte_length ||
+                   byte_length <= max_byte_length - 4)
+                       goto corrupt;
+               newsize = fragment->size + byte_length;
+               data = xrealloc(data, newsize);
+               if (decode_85(data + fragment->size,
+                             buffer + 1,
+                             byte_length))
+                       goto corrupt;
+               fragment->size = newsize;
+               buffer += llen;
+               size -= llen;
+       }
+       fragment->patch = data;
+       return used;
+ corrupt:
+       return error("corrupt binary patch at line %d: %.*s",
+                    linenr-1, llen-1, buffer);
+}
+
 static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
 {
        int hdrsize, patchsize;
@@ -983,19 +1069,34 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                        "Files ",
                        NULL,
                };
+               static const char git_binary[] = "GIT binary patch\n";
                int i;
                int hd = hdrsize + offset;
                unsigned long llen = linelen(buffer + hd, size - hd);
 
-               if (!memcmp(" differ\n", buffer + hd + llen - 8, 8))
+               if (llen == sizeof(git_binary) - 1 &&
+                   !memcmp(git_binary, buffer + hd, llen)) {
+                       int used;
+                       linenr++;
+                       used = parse_binary(buffer + hd + llen,
+                                           size - hd - llen, patch);
+                       if (used)
+                               patchsize = used + llen;
+                       else
+                               patchsize = 0;
+               }
+               else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
                        for (i = 0; binhdr[i]; i++) {
                                int len = strlen(binhdr[i]);
                                if (len < size - hd &&
                                    !memcmp(binhdr[i], buffer + hd, len)) {
+                                       linenr++;
                                        patch->is_binary = 1;
+                                       patchsize = llen;
                                        break;
                                }
                        }
+               }
 
                /* Empty patch cannot be applied if:
                 * - it is a binary patch and we do not do binary_replace, or
@@ -1346,76 +1447,150 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
        return offset;
 }
 
-static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+static char *inflate_it(const void *data, unsigned long size,
+                       unsigned long inflated_size)
+{
+       z_stream stream;
+       void *out;
+       int st;
+
+       memset(&stream, 0, sizeof(stream));
+
+       stream.next_in = (unsigned char *)data;
+       stream.avail_in = size;
+       stream.next_out = out = xmalloc(inflated_size);
+       stream.avail_out = inflated_size;
+       inflateInit(&stream);
+       st = inflate(&stream, Z_FINISH);
+       if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
+               free(out);
+               return NULL;
+       }
+       return out;
+}
+
+static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
+{
+       unsigned long dst_size;
+       struct fragment *fragment = patch->fragments;
+       void *data;
+       void *result;
+
+       data = inflate_it(fragment->patch, fragment->size,
+                         patch->deflate_origlen);
+       if (!data)
+               return error("corrupt patch data");
+       switch (patch->is_binary) {
+       case BINARY_DELTA_DEFLATED:
+               result = patch_delta(desc->buffer, desc->size,
+                                    data,
+                                    patch->deflate_origlen,
+                                    &dst_size);
+               free(desc->buffer);
+               desc->buffer = result;
+               free(data);
+               break;
+       case BINARY_LITERAL_DEFLATED:
+               free(desc->buffer);
+               desc->buffer = data;
+               dst_size = patch->deflate_origlen;
+               break;
+       }
+       if (!desc->buffer)
+               return -1;
+       desc->size = desc->alloc = dst_size;
+       return 0;
+}
+
+static int apply_binary(struct buffer_desc *desc, struct patch *patch)
 {
-       struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
+       unsigned char sha1[20];
+       unsigned char hdr[50];
+       int hdrlen;
 
-       if (patch->is_binary) {
-               unsigned char sha1[20];
+       if (!allow_binary_replacement)
+               return error("cannot apply binary patch to '%s' "
+                            "without --allow-binary-replacement",
+                            name);
 
-               if (!allow_binary_replacement)
-                       return error("cannot apply binary patch to '%s' "
-                                    "without --allow-binary-replacement",
-                                    name);
+       /* For safety, we require patch index line to contain
+        * full 40-byte textual SHA1 for old and new, at least for now.
+        */
+       if (strlen(patch->old_sha1_prefix) != 40 ||
+           strlen(patch->new_sha1_prefix) != 40 ||
+           get_sha1_hex(patch->old_sha1_prefix, sha1) ||
+           get_sha1_hex(patch->new_sha1_prefix, sha1))
+               return error("cannot apply binary patch to '%s' "
+                            "without full index line", name);
 
-               /* For safety, we require patch index line to contain
-                * full 40-byte textual SHA1 for old and new, at least for now.
+       if (patch->old_name) {
+               /* See if the old one matches what the patch
+                * applies to.
                 */
-               if (strlen(patch->old_sha1_prefix) != 40 ||
-                   strlen(patch->new_sha1_prefix) != 40 ||
-                   get_sha1_hex(patch->old_sha1_prefix, sha1) ||
-                   get_sha1_hex(patch->new_sha1_prefix, sha1))
-                       return error("cannot apply binary patch to '%s' "
-                                    "without full index line", name);
-
-               if (patch->old_name) {
-                       unsigned char hdr[50];
-                       int hdrlen;
-
-                       /* See if the old one matches what the patch
-                        * applies to.
-                        */
-                       write_sha1_file_prepare(desc->buffer, desc->size,
-                                               blob_type, sha1, hdr, &hdrlen);
-                       if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
-                               return error("the patch applies to '%s' (%s), "
-                                            "which does not match the "
-                                            "current contents.",
-                                            name, sha1_to_hex(sha1));
-               }
-               else {
-                       /* Otherwise, the old one must be empty. */
-                       if (desc->size)
-                               return error("the patch applies to an empty "
-                                            "'%s' but it is not empty", name);
-               }
+               write_sha1_file_prepare(desc->buffer, desc->size,
+                                       blob_type, sha1, hdr, &hdrlen);
+               if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
+                       return error("the patch applies to '%s' (%s), "
+                                    "which does not match the "
+                                    "current contents.",
+                                    name, sha1_to_hex(sha1));
+       }
+       else {
+               /* Otherwise, the old one must be empty. */
+               if (desc->size)
+                       return error("the patch applies to an empty "
+                                    "'%s' but it is not empty", name);
+       }
+
+       get_sha1_hex(patch->new_sha1_prefix, sha1);
+       if (!memcmp(sha1, null_sha1, 20)) {
+               free(desc->buffer);
+               desc->alloc = desc->size = 0;
+               desc->buffer = NULL;
+               return 0; /* deletion patch */
+       }
 
-               /* For now, we do not record post-image data in the patch,
-                * and require the object already present in the recipient's
-                * object database.
+       if (has_sha1_file(sha1)) {
+               /* We already have the postimage */
+               char type[10];
+               unsigned long size;
+
+               free(desc->buffer);
+               desc->buffer = read_sha1_file(sha1, type, &size);
+               if (!desc->buffer)
+                       return error("the necessary postimage %s for "
+                                    "'%s' cannot be read",
+                                    patch->new_sha1_prefix, name);
+               desc->alloc = desc->size = size;
+       }
+       else {
+               /* We have verified desc matches the preimage;
+                * apply the patch data to it, which is stored
+                * in the patch->fragments->{patch,size}.
                 */
-               if (desc->buffer) {
-                       free(desc->buffer);
-                       desc->alloc = desc->size = 0;
-               }
-               get_sha1_hex(patch->new_sha1_prefix, sha1);
-
-               if (memcmp(sha1, null_sha1, 20)) {
-                       char type[10];
-                       unsigned long size;
-
-                       desc->buffer = read_sha1_file(sha1, type, &size);
-                       if (!desc->buffer)
-                               return error("the necessary postimage %s for "
-                                            "'%s' does not exist",
-                                            patch->new_sha1_prefix, name);
-                       desc->alloc = desc->size = size;
-               }
+               if (apply_binary_fragment(desc, patch))
+                       return error("binary patch does not apply to '%s'",
+                                    name);
 
-               return 0;
+               /* verify that the result matches */
+               write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
+                                       sha1, hdr, &hdrlen);
+               if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
+                       return error("binary patch to '%s' creates incorrect result", name);
        }
 
+       return 0;
+}
+
+static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+{
+       struct fragment *frag = patch->fragments;
+       const char *name = patch->old_name ? patch->old_name : patch->new_name;
+
+       if (patch->is_binary)
+               return apply_binary(desc, patch);
+
        while (frag) {
                if (apply_one_fragment(desc, frag) < 0)
                        return error("patch failed: %s:%ld",
@@ -1993,7 +2168,8 @@ int main(int argc, char **argv)
                        diffstat = 1;
                        continue;
                }
-               if (!strcmp(arg, "--allow-binary-replacement")) {
+               if (!strcmp(arg, "--allow-binary-replacement") ||
+                   !strcmp(arg, "--binary")) {
                        allow_binary_replacement = 1;
                        continue;
                }
diff --git a/base85.c b/base85.c
new file mode 100644 (file)
index 0000000..b97f7f9
--- /dev/null
+++ b/base85.c
@@ -0,0 +1,134 @@
+#include "cache.h"
+
+#undef DEBUG_85
+
+#ifdef DEBUG_85
+#define say(a) fprintf(stderr, a)
+#define say1(a,b) fprintf(stderr, a, b)
+#define say2(a,b,c) fprintf(stderr, a, b, c)
+#else
+#define say(a) do {} while(0)
+#define say1(a,b) do {} while(0)
+#define say2(a,b,c) do {} while(0)
+#endif
+
+static const char en85[] = {
+       '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+       'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+       'U', 'V', 'W', 'X', 'Y', 'Z',
+       'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+       'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+       'u', 'v', 'w', 'x', 'y', 'z',
+       '!', '#', '$', '%', '&', '(', ')', '*', '+', '-',
+       ';', '<', '=', '>', '?', '@', '^', '_', '`', '{',
+       '|', '}', '~'
+};
+
+static char de85[256];
+static void prep_base85(void)
+{
+       int i;
+       if (de85['Z'])
+               return;
+       for (i = 0; i < ARRAY_SIZE(en85); i++) {
+               int ch = en85[i];
+               de85[ch] = i + 1;
+       }
+}
+
+int decode_85(char *dst, char *buffer, int len)
+{
+       prep_base85();
+
+       say2("decode 85 <%.*s>", len/4*5, buffer);
+       while (len) {
+               unsigned acc = 0;
+               int cnt;
+               for (cnt = 0; cnt < 5; cnt++, buffer++) {
+                       int ch = *((unsigned char *)buffer);
+                       int de = de85[ch];
+                       if (!de)
+                               return error("invalid base85 alphabet %c", ch);
+                       de--;
+                       if (cnt == 4) {
+                               /*
+                                * Detect overflow.  The largest
+                                * 5-letter possible is "|NsC0" to
+                                * encode 0xffffffff, and "|NsC" gives
+                                * 0x03030303 at this point (i.e.
+                                * 0xffffffff = 0x03030303 * 85).
+                                */
+                               if (0x03030303 < acc ||
+                                   (0x03030303 == acc && de))
+                                       error("invalid base85 sequence %.5s",
+                                             buffer-3);
+                       }
+                       acc = acc * 85 + de;
+                       say1(" <%08x>", acc);
+               }
+               say1(" %08x", acc);
+               for (cnt = 0; cnt < 4 && len; cnt++, len--) {
+                       *dst++ = (acc >> 24) & 0xff;
+                       acc = acc << 8;
+               }
+       }
+       say("\n");
+
+       return 0;
+}
+
+void encode_85(char *buf, unsigned char *data, int bytes)
+{
+       prep_base85();
+
+       say("encode 85");
+       while (bytes) {
+               unsigned acc = 0;
+               int cnt;
+               for (cnt = 0; cnt < 4 && bytes; cnt++, bytes--) {
+                       int ch = *data++;
+                       acc |= ch << ((3-cnt)*8);
+               }
+               say1(" %08x", acc);
+               for (cnt = 0; cnt < 5; cnt++) {
+                       int val = acc % 85;
+                       acc /= 85;
+                       buf[4-cnt] = en85[val];
+               }
+               buf += 5;
+       }
+       say("\n");
+
+       *buf = 0;
+}
+
+#ifdef DEBUG_85
+int main(int ac, char **av)
+{
+       char buf[1024];
+
+       if (!strcmp(av[1], "-e")) {
+               int len = strlen(av[2]);
+               encode_85(buf, av[2], len);
+               if (len <= 26) len = len + 'A' - 1;
+               else len = len + 'a' - 26 + 1;
+               printf("encoded: %c%s\n", len, buf);
+               return 0;
+       }
+       if (!strcmp(av[1], "-d")) {
+               int len = *av[2];
+               if ('A' <= len && len <= 'Z') len = len - 'A' + 1;
+               else len = len - 'a' + 26 + 1;
+               decode_85(buf, av[2]+1, len);
+               printf("decoded: %.*s\n", len, buf);
+               return 0;
+       }
+       if (!strcmp(av[1], "-t")) {
+               char t[4] = { -1,-1,-1,-1 };
+               encode_85(buf, t, 4);
+               printf("encoded: D%s\n", buf);
+               return 0;
+       }
+}
+#endif
index 06d06ff310da61d71cc11bf0ac9245228a6cc10b..e530022824c13138925152de716beb3573714f40 100644 (file)
@@ -72,7 +72,7 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
 {
        int n = 0;
        FILE *f = fopen(git_path("remotes/%s", repo), "r");
-       int has_explicit_refspec = refspec_nr;
+       int has_explicit_refspec = refspec_nr || all || tags;
 
        if (!f)
                return -1;
@@ -144,7 +144,7 @@ static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
        config_repo = repo;
        config_current_uri = 0;
        config_uri = uri;
-       config_get_refspecs = !refspec_nr;
+       config_get_refspecs = !(refspec_nr || all || tags);
 
        git_config(get_remote_config);
        return config_current_uri;
diff --git a/cache.h b/cache.h
index d186b44a516bbfb5a3f4b1c6687b5c19e64fa6ac..b1300cd989437e4631d6e4a58b1c32941b5c3b6b 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -365,4 +365,8 @@ extern int receive_keep_pack(int fd[2], const char *me, int quiet);
 /* pager.c */
 extern void setup_pager(void);
 
+/* base85 */
+int decode_85(char *dst, char *line, int linelen);
+void encode_85(char *buf, unsigned char *data, int bytes);
+
 #endif /* CACHE_H */
index 47734140dd4e44b88a9bf620dcd628381e1dd5f3..87fb22041ec4b068cf1072689bc8309d8804ae1e 100644 (file)
--- a/config.c
+++ b/config.c
@@ -335,8 +335,9 @@ static int store_aux(const char* key, const char* value)
                        store.offset[store.seen] = ftell(config_file);
                        store.state = KEY_SEEN;
                        store.seen++;
-               } else if(!strncmp(key, store.key, store.baselen))
-                       store.state = SECTION_SEEN;
+               } else if (strrchr(key, '.') - key == store.baselen &&
+                             !strncmp(key, store.key, store.baselen))
+                                       store.state = SECTION_SEEN;
        }
        return 0;
 }
index 7c44450d72021a91069697826227922a841cd472..de13a96b8a66c97fdc2e5822986ea48285fa9b6d 100755 (executable)
@@ -8,7 +8,7 @@ use vars qw/    $AUTHOR $VERSION
                $GIT_SVN_INDEX $GIT_SVN
                $GIT_DIR $REV_DIR/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '0.11.0';
+$VERSION = '1.0.0';
 
 use Cwd qw/abs_path/;
 $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
@@ -42,7 +42,8 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
 my %cmd = (
        fetch => [ \&fetch, "Download new revisions from SVN",
                        { 'revision|r=s' => \$_revision, %fc_opts } ],
-       init => [ \&init, "Initialize and fetch (import)", { } ],
+       init => [ \&init, "Initialize a repo for tracking" .
+                         " (requires URL argument)", { } ],
        commit => [ \&commit, "Commit git revisions to SVN",
                        {       'stdin|' => \$_stdin,
                                'edit|e' => \$_edit,
@@ -220,7 +221,8 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN
 }
 
 sub init {
-       $SVN_URL = shift or croak "SVN repository location required\n";
+       $SVN_URL = shift or die "SVN repository location required " .
+                               "as a command-line argument\n";
        unless (-d $GIT_DIR) {
                sys('git-init-db');
        }
index e18fcaf4fb542ae5b5b2921fbc9abd62dbd7eb92..f7d3de48f0858e0210e0e982836ff589a6871585 100644 (file)
@@ -36,17 +36,22 @@ COMMANDS
 --------
 init::
        Creates an empty git repository with additional metadata
-       directories for git-svn.  The SVN_URL must be specified
-       at this point.
+       directories for git-svn.  The Subversion URL must be specified
+       as a command-line argument.
 
 fetch::
-       Fetch unfetched revisions from the SVN_URL we are tracking.
-       refs/heads/remotes/git-svn will be updated to the latest revision.
+       Fetch unfetched revisions from the Subversion URL we are
+       tracking.  refs/remotes/git-svn will be updated to the
+       latest revision.
 
-       Note: You should never attempt to modify the remotes/git-svn branch
-       outside of git-svn.  Instead, create a branch from remotes/git-svn
-       and work on that branch.  Use the 'commit' command (see below)
-       to write git commits back to remotes/git-svn.
+       Note: You should never attempt to modify the remotes/git-svn
+       branch outside of git-svn.  Instead, create a branch from
+       remotes/git-svn and work on that branch.  Use the 'commit'
+       command (see below) to write git commits back to
+       remotes/git-svn.
+
+       See 'Additional Fetch Arguments' if you are interested in
+       manually joining branches on commit.
 
 commit::
        Commit specified commit or tree objects to SVN.  This relies on
@@ -62,9 +67,9 @@ rebuild::
        tracked with git-svn.  Unfortunately, git-clone does not clone
        git-svn metadata and the svn working tree that git-svn uses for
        its operations.  This rebuilds the metadata so git-svn can
-       resume fetch operations.  SVN_URL may be optionally specified if
-       the directory/repository you're tracking has moved or changed
-       protocols.
+       resume fetch operations.  A Subversion URL may be optionally
+       specified at the command-line if the directory/repository you're
+       tracking has moved or changed protocols.
 
 show-ignore::
        Recursively finds and lists the svn:ignore property on
@@ -123,6 +128,24 @@ OPTIONS
        repo-config key: svn.l
        repo-config key: svn.findcopiesharder
 
+-A<filename>::
+--authors-file=<filename>::
+
+       Syntax is compatible with the files used by git-svnimport and
+       git-cvsimport:
+
+------------------------------------------------------------------------
+loginname = Joe User <user@example.com>
+------------------------------------------------------------------------
+
+       If this option is specified and git-svn encounters an SVN
+       committer name that does not exist in the authors-file, git-svn
+       will abort operation. The user will then have to add the
+       appropriate entry.  Re-running the previous git-svn command
+       after the authors-file is modified should continue operation.
+
+       repo-config key: svn.authors-file
+
 ADVANCED OPTIONS
 ----------------
 -b<refname>::
diff --git a/diff.c b/diff.c
index c845c871130a8943c3c5f5d46cfd52426180fc8b..bfe54c3e093439d8e9df55fa319715ca20090f99 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -8,6 +8,7 @@
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "delta.h"
 #include "xdiff-interface.h"
 
 static int use_size_cache;
@@ -391,6 +392,90 @@ static void show_stats(struct diffstat_t* data)
                        total_files, adds, dels);
 }
 
+static unsigned char *deflate_it(char *data,
+                                unsigned long size,
+                                unsigned long *result_size)
+{
+       int bound;
+       unsigned char *deflated;
+       z_stream stream;
+
+       memset(&stream, 0, sizeof(stream));
+       deflateInit(&stream, Z_BEST_COMPRESSION);
+       bound = deflateBound(&stream, size);
+       deflated = xmalloc(bound);
+       stream.next_out = deflated;
+       stream.avail_out = bound;
+
+       stream.next_in = (unsigned char *)data;
+       stream.avail_in = size;
+       while (deflate(&stream, Z_FINISH) == Z_OK)
+               ; /* nothing */
+       deflateEnd(&stream);
+       *result_size = stream.total_out;
+       return deflated;
+}
+
+static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+{
+       void *cp;
+       void *delta;
+       void *deflated;
+       void *data;
+       unsigned long orig_size;
+       unsigned long delta_size;
+       unsigned long deflate_size;
+       unsigned long data_size;
+
+       printf("GIT binary patch\n");
+       /* We could do deflated delta, or we could do just deflated two,
+        * whichever is smaller.
+        */
+       delta = NULL;
+       deflated = deflate_it(two->ptr, two->size, &deflate_size);
+       if (one->size && two->size) {
+               delta = diff_delta(one->ptr, one->size,
+                                  two->ptr, two->size,
+                                  &delta_size, deflate_size);
+               if (delta) {
+                       void *to_free = delta;
+                       orig_size = delta_size;
+                       delta = deflate_it(delta, delta_size, &delta_size);
+                       free(to_free);
+               }
+       }
+
+       if (delta && delta_size < deflate_size) {
+               printf("delta %lu\n", orig_size);
+               free(deflated);
+               data = delta;
+               data_size = delta_size;
+       }
+       else {
+               printf("literal %lu\n", two->size);
+               free(delta);
+               data = deflated;
+               data_size = deflate_size;
+       }
+
+       /* emit data encoded in base85 */
+       cp = data;
+       while (data_size) {
+               int bytes = (52 < data_size) ? 52 : data_size;
+               char line[70];
+               data_size -= bytes;
+               if (bytes <= 26)
+                       line[0] = bytes + 'A' - 1;
+               else
+                       line[0] = bytes - 26 + 'a' - 1;
+               encode_85(line + 1, cp, bytes);
+               cp += bytes;
+               puts(line);
+       }
+       printf("\n");
+       free(data);
+}
+
 #define FIRST_FEW_BYTES 8000
 static int mmfile_is_binary(mmfile_t *mf)
 {
@@ -407,6 +492,7 @@ static void builtin_diff(const char *name_a,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
+                        struct diff_options *o,
                         int complete_rewrite)
 {
        mmfile_t mf1, mf2;
@@ -451,8 +537,17 @@ static void builtin_diff(const char *name_a,
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
 
-       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
-               printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
+       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+               /* Quite common confusing case */
+               if (mf1.size == mf2.size &&
+                   !memcmp(mf1.ptr, mf2.ptr, mf1.size))
+                       goto free_ab_and_return;
+               if (o->binary)
+                       emit_binary_diff(&mf1, &mf2);
+               else
+                       printf("Binary files %s and %s differ\n",
+                              lbl[0], lbl[1]);
+       }
        else {
                /* Crazy xdl interfaces.. */
                const char *diffopts = getenv("GIT_DIFF_OPTS");
@@ -928,6 +1023,7 @@ static void run_diff_cmd(const char *pgm,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
+                        struct diff_options *o,
                         int complete_rewrite)
 {
        if (pgm) {
@@ -937,7 +1033,7 @@ static void run_diff_cmd(const char *pgm,
        }
        if (one && two)
                builtin_diff(name, other ? other : name,
-                            one, two, xfrm_msg, complete_rewrite);
+                            one, two, xfrm_msg, o, complete_rewrite);
        else
                printf("* Unmerged path %s\n", name);
 }
@@ -971,7 +1067,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
-               run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+               run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
                return;
        }
 
@@ -1041,14 +1137,14 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
                 * needs to be split into deletion and creation.
                 */
                struct diff_filespec *null = alloc_filespec(two->path);
-               run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+               run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
                free(null);
                null = alloc_filespec(one->path);
-               run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+               run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
                free(null);
        }
        else
-               run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
+               run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
                             complete_rewrite);
 
        free(name_munged);
@@ -1147,6 +1243,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->rename_limit = strtoul(arg+2, NULL, 10);
        else if (!strcmp(arg, "--full-index"))
                options->full_index = 1;
+       else if (!strcmp(arg, "--binary")) {
+               options->output_format = DIFF_FORMAT_PATCH;
+               options->full_index = options->binary = 1;
+       }
        else if (!strcmp(arg, "--name-only"))
                options->output_format = DIFF_FORMAT_NAME;
        else if (!strcmp(arg, "--name-status"))
diff --git a/diff.h b/diff.h
index b3b2c4dd28c4f1a8f14948e5d1262dd71f1e96cf..d052608404314880652561e4296e05e9da3f96ca 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -28,6 +28,7 @@ struct diff_options {
                 with_raw:1,
                 with_stat:1,
                 tree_in_recursive:1,
+                binary:1,
                 full_index:1,
                 silent_on_remove:1,
                 find_copies_harder:1;
index 5b2ef9a51387dc01e9d31f08962101cf1323e3e2..523a1c7da8f1baf70d2f700dbafa8e78f9a1c4b3 100644 (file)
@@ -1243,6 +1243,7 @@ int main(int argc, char **argv)
 
        setup_git_directory();
 
+       progress = isatty(2);
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
@@ -1273,6 +1274,10 @@ int main(int argc, char **argv)
                                        usage(pack_usage);
                                continue;
                        }
+                       if (!strcmp("--progress", arg)) {
+                               progress = 1;
+                               continue;
+                       }
                        if (!strcmp("-q", arg)) {
                                progress = 0;
                                continue;