Merge branch 'js/bundle'
authorJunio C Hamano <junkio@cox.net>
Wed, 28 Feb 2007 22:38:36 +0000 (14:38 -0800)
committerJunio C Hamano <junkio@cox.net>
Wed, 28 Feb 2007 22:38:36 +0000 (14:38 -0800)
* js/bundle:
bundle: reword missing prerequisite error message
git-bundle: record commit summary in the prerequisite data
git-bundle: fix 'create --all'
git-bundle: avoid fork() in verify_bundle()
git-bundle: assorted fixes
Add git-bundle: move objects and references by archive

1  2 
.gitignore
Documentation/cmd-list.perl
Makefile
builtin.h
git-fetch.sh
git.c
index-pack.c
revision.c
diff --combined .gitignore
index eb8a1f860678ff86f3b9ca95f618226551c884cf,9c912c11c148134ff6bbc55df718af1f8d0c1af6..0eaba0a278df33fbef7f247ce267181d7ccba5f0
@@@ -13,6 -13,7 +13,7 @@@ git-archiv
  git-bisect
  git-blame
  git-branch
+ git-bundle
  git-cat-file
  git-check-ref-format
  git-checkout
@@@ -33,6 -34,7 +34,6 @@@ git-daemo
  git-diff
  git-diff-files
  git-diff-index
 -git-diff-stages
  git-diff-tree
  git-describe
  git-fast-import
@@@ -100,6 -102,7 +101,6 @@@ git-repo-confi
  git-request-pull
  git-rerere
  git-reset
 -git-resolve
  git-rev-list
  git-rev-parse
  git-revert
@@@ -139,7 -142,6 +140,7 @@@ git-whatchange
  git-write-tree
  git-core-*/?*
  gitweb/gitweb.cgi
 +test-chmtime
  test-date
  test-delta
  test-dump-cache-tree
index a2d6268e2b4b099b0b080d381ada189ffda592fa,29f81f6ecf449527f5af9dcefdb1386281e2c3cf..f61c77aa7c360e12a3e11e9e5dd3ebecfad78d7e
@@@ -70,6 -70,7 +70,7 @@@ git-archiv
  git-bisect                              mainporcelain
  git-blame                               ancillaryinterrogators
  git-branch                              mainporcelain
+ git-bundle                              mainporcelain
  git-cat-file                            plumbinginterrogators
  git-checkout-index                      plumbingmanipulators
  git-checkout                            mainporcelain
@@@ -90,6 -91,7 +91,6 @@@ git-describ
  git-diff-files                          plumbinginterrogators
  git-diff-index                          plumbinginterrogators
  git-diff                                mainporcelain
 -git-diff-stages                         plumbinginterrogators
  git-diff-tree                           plumbinginterrogators
  git-fast-import                               ancillarymanipulators
  git-fetch                               mainporcelain
@@@ -145,10 -147,10 +146,10 @@@ git-reflo
  git-relink                              ancillarymanipulators
  git-repack                              ancillarymanipulators
  git-config                              ancillarymanipulators
 +git-remote                              ancillarymanipulators
  git-request-pull                        foreignscminterface
  git-rerere                              ancillaryinterrogators
  git-reset                               mainporcelain
 -git-resolve                             mainporcelain
  git-revert                              mainporcelain
  git-rev-list                            plumbinginterrogators
  git-rev-parse                           ancillaryinterrogators
diff --combined Makefile
index 8a42be9babb04a2a9f6eb0fba24a5fc8b5fe59a1,a7869418b81295d7e8707027a31525b9b3173019..23ab7d6f9f6554169de554b53042bc2d99bf4fed
+++ b/Makefile
@@@ -28,10 -28,6 +28,10 @@@ all:
  #
  # Define NO_STRLCPY if you don't have strlcpy.
  #
 +# Define NO_STRTOUMAX if you don't have strtoumax in the C library.
 +# If your compiler also does not support long long or does not have
 +# strtoull, define NO_STRTOULL.
 +#
  # Define NO_SETENV if you don't have setenv in the C library.
  #
  # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
@@@ -128,7 -124,6 +128,7 @@@ prefix = $(HOME
  bindir = $(prefix)/bin
  gitexecdir = $(bindir)
  template_dir = $(prefix)/share/git-core/templates/
 +ETC_GITCONFIG = $(prefix)/etc/gitconfig
  # DESTDIR=
  
  # default configuration for gitweb
@@@ -177,7 -172,7 +177,7 @@@ SCRIPT_SH = 
        git-merge-one-file.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh \
        git-repack.sh git-request-pull.sh git-reset.sh \
 -      git-resolve.sh git-revert.sh git-sh-setup.sh \
 +      git-revert.sh git-sh-setup.sh \
        git-tag.sh git-verify-tag.sh \
        git-applymbox.sh git-applypatch.sh git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@@@ -267,8 -262,7 +267,8 @@@ LIB_OBJS = 
        revision.o pager.o tree-walk.o xdiff-interface.o \
        write_or_die.o trace.o list-objects.o grep.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
 -      color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o
 +      color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
 +      convert.o
  
  BUILTIN_OBJS = \
        builtin-add.o \
        builtin-archive.o \
        builtin-blame.o \
        builtin-branch.o \
+       builtin-bundle.o \
        builtin-cat-file.o \
        builtin-checkout-index.o \
        builtin-check-ref-format.o \
        builtin-diff.o \
        builtin-diff-files.o \
        builtin-diff-index.o \
 -      builtin-diff-stages.o \
        builtin-diff-tree.o \
        builtin-fmt-merge-msg.o \
        builtin-for-each-ref.o \
        builtin-ls-tree.o \
        builtin-mailinfo.o \
        builtin-mailsplit.o \
 +      builtin-merge-base.o \
        builtin-merge-file.o \
        builtin-mv.o \
        builtin-name-rev.o \
@@@ -359,13 -354,11 +360,13 @@@ ifeq ($(uname_S),SunOS
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
 +              NO_STRTOUMAX = YesPlease
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
                NO_C99_FORMAT = YesPlease
 +              NO_STRTOUMAX = YesPlease
        endif
        INSTALL = ginstall
        TAR = gtar
@@@ -525,13 -518,6 +526,13 @@@ ifdef NO_STRLCP
        COMPAT_CFLAGS += -DNO_STRLCPY
        COMPAT_OBJS += compat/strlcpy.o
  endif
 +ifdef NO_STRTOUMAX
 +      COMPAT_CFLAGS += -DNO_STRTOUMAX
 +      COMPAT_OBJS += compat/strtoumax.o
 +endif
 +ifdef NO_STRTOULL
 +      COMPAT_CFLAGS += -DNO_STRTOULL
 +endif
  ifdef NO_SETENV
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
@@@ -599,7 -585,6 +600,7 @@@ endi
  # Shell quote (do not use $(call) to accommodate ancient setups);
  
  SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 +ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
  
  DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
  bindir_SQ = $(subst ','\'',$(bindir))
@@@ -612,8 -597,7 +613,8 @@@ PERL_PATH_SQ = $(subst ','\'',$(PERL_PA
  
  LIBS = $(GITLIBS) $(EXTLIBS)
  
 -BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
 +BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
 +      -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $(COMPAT_CFLAGS)
  LIB_OBJS += $(COMPAT_OBJS)
  
  ALL_CFLAGS += $(BASIC_CFLAGS)
@@@ -829,7 -813,7 +830,7 @@@ GIT-CFLAGS: .FORCE-GIT-CFLAG
  
  export NO_SVN_TESTS
  
 -test: all
 +test: all test-chmtime$X
        $(MAKE) -C t/ all
  
  test-date$X: test-date.c date.o ctype.o
@@@ -844,9 -828,6 +845,9 @@@ test-dump-cache-tree$X: dump-cache-tree
  test-sha1$X: test-sha1.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 +test-chmtime$X: test-chmtime.c
 +      $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
 +
  check-sha1:: test-sha1$X
        ./test-sha1.sh
  
@@@ -902,8 -883,7 +903,8 @@@ dist: git.spec git-archiv
        $(TAR) rf $(GIT_TARNAME).tar \
                $(GIT_TARNAME)/git.spec \
                $(GIT_TARNAME)/version \
 -              $(GIT_TARNAME)/git-gui/version
 +              $(GIT_TARNAME)/git-gui/version \
 +              $(GIT_TARNAME)/git-gui/credits
        @rm -rf $(GIT_TARNAME)
        gzip -f -9 $(GIT_TARNAME).tar
  
@@@ -960,14 -940,11 +961,14 @@@ check-docs:
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-stupid | \
 +              git-add--interactive | git-fsck-objects | git-init-db | \
 +              git-repo-config | \
                git-ssh-pull | git-ssh-push ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
                echo "no doc: $$v"; \
 -              grep -q "^gitlink:$$v\[[0-9]\]::" Documentation/git.txt || \
 +              sed -e '1,/^__DATA__/d' Documentation/cmd-list.perl | \
 +              grep -q "^$$v[  ]" || \
                case "$$v" in \
                git) ;; \
                *) echo "no link: $$v";; \
diff --combined builtin.h
index 57e8741ff0569a349a48e386fae2664fe474a546,8ceb0dce9709821cdd9895b255d14ba30f77e49c..528074b61508fbf25fe0fdc7fbfa7a5bb9930a25
+++ b/builtin.h
@@@ -19,6 -19,7 +19,7 @@@ extern int cmd_apply(int argc, const ch
  extern int cmd_archive(int argc, const char **argv, const char *prefix);
  extern int cmd_blame(int argc, const char **argv, const char *prefix);
  extern int cmd_branch(int argc, const char **argv, const char *prefix);
+ extern int cmd_bundle(int argc, const char **argv, const char *prefix);
  extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
  extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
  extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
@@@ -29,6 -30,7 +30,6 @@@ extern int cmd_describe(int argc, cons
  extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
  extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
  extern int cmd_diff(int argc, const char **argv, const char *prefix);
 -extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);
  extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
  extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
@@@ -44,7 -46,6 +45,7 @@@ extern int cmd_ls_files(int argc, cons
  extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
  extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 +extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
  extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
  extern int cmd_mv(int argc, const char **argv, const char *prefix);
  extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
diff --combined git-fetch.sh
index d230995f6e3033456b670fa60b8a392dbc8dd08f,d5b34bc0e2404c96a1eee2aefd7daa5e5945b07e..59bee5db0f0738cd6d43772e7279edf726a0b7d1
@@@ -243,15 -243,6 +243,15 @@@ the
        orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
  fi
  
 +# Allow --notags from remote.$1.tagopt
 +case "$tags$no_tags" in
 +'')
 +      case "$(git-config --get "remote.$1.tagopt")" in
 +      --no-tags)
 +              no_tags=t ;;
 +      esac
 +esac
 +
  # If --tags (and later --heads or --all) is specified, then we are
  # not talking about defaults stored in Pull: line of remotes or
  # branches file, and just fetch those and refspecs explicitly given.
@@@ -386,8 -377,15 +386,15 @@@ fetch_main () 
      ( : subshell because we muck with IFS
        IFS="   $LF"
        (
+       if test -f "$remote" ; then
+           test -n "$shallow_depth" &&
+               die "shallow clone with bundle is not supported"
+           git-bundle unbundle "$remote" $rref ||
+           echo failed "$remote"
+       else
          git-fetch-pack --thin $exec $keep $shallow_depth "$remote" $rref ||
          echo failed "$remote"
+       fi
        ) |
        (
        trap '
diff --combined git.c
index fa3fd11386eac14d0aed918e88d5dc449ef1b37f,cfa57992d0386b8db3e24172a9ec1bfe22d49f8c..9b37f423216a6e8a86646ba15af642b9f7cc10e6
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -48,7 -48,7 +48,7 @@@ static int handle_options(const char**
                /*
                 * Check remaining flags.
                 */
 -              if (!strncmp(cmd, "--exec-path", 11)) {
 +              if (!prefixcmp(cmd, "--exec-path")) {
                        cmd += 11;
                        if (*cmd == '=')
                                git_set_exec_path(cmd + 1);
@@@ -66,7 -66,7 +66,7 @@@
                        setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
                        (*argv)++;
                        (*argc)--;
 -              } else if (!strncmp(cmd, "--git-dir=", 10)) {
 +              } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                } else if (!strcmp(cmd, "--bare")) {
                        static char git_dir[PATH_MAX+1];
@@@ -88,7 -88,7 +88,7 @@@ static char *alias_string
  
  static int git_alias_config(const char *var, const char *value)
  {
 -      if (!strncmp(var, "alias.", 6) && !strcmp(var + 6, alias_command)) {
 +      if (!prefixcmp(var, "alias.") && !strcmp(var + 6, alias_command)) {
                alias_string = xstrdup(value);
        }
        return 0;
@@@ -229,6 -229,7 +229,7 @@@ static void handle_internal_command(in
                { "archive", cmd_archive },
                { "blame", cmd_blame, RUN_SETUP },
                { "branch", cmd_branch, RUN_SETUP },
+               { "bundle", cmd_bundle },
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout-index", cmd_checkout_index, RUN_SETUP },
                { "check-ref-format", cmd_check_ref_format },
                { "config", cmd_config },
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "describe", cmd_describe, RUN_SETUP },
 -              { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
 -              { "diff-files", cmd_diff_files, RUN_SETUP },
 +              { "diff", cmd_diff, USE_PAGER },
 +              { "diff-files", cmd_diff_files },
                { "diff-index", cmd_diff_index, RUN_SETUP },
 -              { "diff-stages", cmd_diff_stages, RUN_SETUP },
                { "diff-tree", cmd_diff_tree, RUN_SETUP },
                { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
                { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
                { "fsck", cmd_fsck, RUN_SETUP },
                { "fsck-objects", cmd_fsck, RUN_SETUP },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
 -              { "grep", cmd_grep, RUN_SETUP },
 +              { "grep", cmd_grep, RUN_SETUP | USE_PAGER },
                { "help", cmd_help },
                { "init", cmd_init_db },
                { "init-db", cmd_init_db },
                { "ls-tree", cmd_ls_tree, RUN_SETUP },
                { "mailinfo", cmd_mailinfo },
                { "mailsplit", cmd_mailsplit },
 +              { "merge-base", cmd_merge_base, RUN_SETUP },
                { "merge-file", cmd_merge_file },
                { "mv", cmd_mv, RUN_SETUP | NOT_BARE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
@@@ -348,7 -349,7 +349,7 @@@ int main(int argc, const char **argv, c
         * So we just directly call the internal command handler, and
         * die if that one cannot handle it.
         */
 -      if (!strncmp(cmd, "git-", 4)) {
 +      if (!prefixcmp(cmd, "git-")) {
                cmd += 4;
                argv[0] = cmd;
                handle_internal_command(argc, argv, envp);
        argc--;
        handle_options(&argv, &argc);
        if (argc > 0) {
 -              if (!strncmp(argv[0], "--", 2))
 +              if (!prefixcmp(argv[0], "--"))
                        argv[0] += 2;
        } else {
                /* Default command: "help" */
diff --combined index-pack.c
index 9f8f0cad20876dd6c9986632c24f9bb9ea01e439,64d75f8c4d592ce39d390ed6e89fa58b9374c68d..cf81a99500e9bf5fc0c0812a2172c5561dc65b68
@@@ -277,19 -277,13 +277,19 @@@ static void *get_data_from_pack(struct 
  {
        unsigned long from = obj[0].offset + obj[0].hdr_size;
        unsigned long len = obj[1].offset - from;
 +      unsigned long rdy = 0;
        unsigned char *src, *data;
        z_stream stream;
        int st;
  
        src = xmalloc(len);
 -      if (pread(pack_fd, src, len, from) != len)
 -              die("cannot pread pack file: %s", strerror(errno));
 +      data = src;
 +      do {
 +              ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
 +              if (n <= 0)
 +                      die("cannot pread pack file: %s", strerror(errno));
 +              rdy += n;
 +      } while (rdy < len);
        data = xmalloc(obj->size);
        memset(&stream, 0, sizeof(stream));
        stream.next_out = data;
@@@ -463,7 -457,8 +463,8 @@@ static void parse_pack_objects(unsigne
        /* If input_fd is a file, we should have reached its end now. */
        if (fstat(input_fd, &st))
                die("cannot fstat packfile: %s", strerror(errno));
-       if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
+       if (S_ISREG(st.st_mode) &&
+                       lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
                die("pack has junk at the end");
  
        if (!nr_deltas)
@@@ -601,23 -596,30 +602,23 @@@ static void fix_unresolved_deltas(int n
                struct delta_entry *d = sorted_by_pos[i];
                void *data;
                unsigned long size;
 -              char type[10];
 -              enum object_type obj_type;
 +              enum object_type type;
                int j, first, last;
  
                if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
                        continue;
 -              data = read_sha1_file(d->base.sha1, type, &size);
 +              data = read_sha1_file(d->base.sha1, &type, &size);
                if (!data)
                        continue;
 -              if      (!strcmp(type, blob_type))   obj_type = OBJ_BLOB;
 -              else if (!strcmp(type, tree_type))   obj_type = OBJ_TREE;
 -              else if (!strcmp(type, commit_type)) obj_type = OBJ_COMMIT;
 -              else if (!strcmp(type, tag_type))    obj_type = OBJ_TAG;
 -              else die("base object %s is of type '%s'",
 -                       sha1_to_hex(d->base.sha1), type);
  
                find_delta_children(&d->base, &first, &last);
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
                        if (child->real_type == OBJ_REF_DELTA)
 -                              resolve_delta(child, data, size, obj_type);
 +                              resolve_delta(child, data, size, type);
                }
  
 -              append_obj_to_pack(data, size, obj_type);
 +              append_obj_to_pack(data, size, type);
                free(data);
                if (verbose)
                        percent = display_progress(nr_resolved_deltas,
@@@ -848,9 -850,9 +849,9 @@@ int main(int argc, char **argv
                                fix_thin_pack = 1;
                        } else if (!strcmp(arg, "--keep")) {
                                keep_msg = "";
 -                      } else if (!strncmp(arg, "--keep=", 7)) {
 +                      } else if (!prefixcmp(arg, "--keep=")) {
                                keep_msg = arg + 7;
 -                      } else if (!strncmp(arg, "--pack_header=", 14)) {
 +                      } else if (!prefixcmp(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
  
diff --combined revision.c
index b84c066cbac33bb8b4103e8be0de5959a02a643c,c1bf5ea035e049caef67ddf6296b138d68a83184..f5b8ae4f031a059cff08328cf661515b9e68ccec
@@@ -116,8 -116,6 +116,8 @@@ void mark_parents_uninteresting(struct 
  
  void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
  {
 +      if (revs->no_walk && (obj->flags & UNINTERESTING))
 +              die("object ranges do not make sense when not walking revisions");
        add_object_array(obj, name, &revs->pending);
        if (revs->reflog_info && obj->type == OBJ_COMMIT)
                add_reflog_for_walk(revs->reflog_info,
@@@ -482,7 -480,7 +482,7 @@@ static int handle_one_ref(const char *p
        struct all_refs_cb *cb = cb_data;
        struct object *object = get_reference(cb->all_revs, path, sha1,
                                              cb->all_flags);
-       add_pending_object(cb->all_revs, object, "");
+       add_pending_object(cb->all_revs, object, path);
        return 0;
  }
  
@@@ -815,11 -813,11 +815,11 @@@ int setup_revisions(int argc, const cha
                const char *arg = argv[i];
                if (*arg == '-') {
                        int opts;
 -                      if (!strncmp(arg, "--max-count=", 12)) {
 +                      if (!prefixcmp(arg, "--max-count=")) {
                                revs->max_count = atoi(arg + 12);
                                continue;
                        }
 -                      if (!strncmp(arg, "--skip=", 7)) {
 +                      if (!prefixcmp(arg, "--skip=")) {
                                revs->skip_count = atoi(arg + 7);
                                continue;
                        }
                                revs->max_count = atoi(argv[++i]);
                                continue;
                        }
 -                      if (!strncmp(arg,"-n",2)) {
 +                      if (!prefixcmp(arg, "-n")) {
                                revs->max_count = atoi(arg + 2);
                                continue;
                        }
 -                      if (!strncmp(arg, "--max-age=", 10)) {
 +                      if (!prefixcmp(arg, "--max-age=")) {
                                revs->max_age = atoi(arg + 10);
                                continue;
                        }
 -                      if (!strncmp(arg, "--since=", 8)) {
 +                      if (!prefixcmp(arg, "--since=")) {
                                revs->max_age = approxidate(arg + 8);
                                continue;
                        }
 -                      if (!strncmp(arg, "--after=", 8)) {
 +                      if (!prefixcmp(arg, "--after=")) {
                                revs->max_age = approxidate(arg + 8);
                                continue;
                        }
 -                      if (!strncmp(arg, "--min-age=", 10)) {
 +                      if (!prefixcmp(arg, "--min-age=")) {
                                revs->min_age = atoi(arg + 10);
                                continue;
                        }
 -                      if (!strncmp(arg, "--before=", 9)) {
 +                      if (!prefixcmp(arg, "--before=")) {
                                revs->min_age = approxidate(arg + 9);
                                continue;
                        }
 -                      if (!strncmp(arg, "--until=", 8)) {
 +                      if (!prefixcmp(arg, "--until=")) {
                                revs->min_age = approxidate(arg + 8);
                                continue;
                        }
                                revs->num_ignore_packed = 0;
                                continue;
                        }
 -                      if (!strncmp(arg, "--unpacked=", 11)) {
 +                      if (!prefixcmp(arg, "--unpacked=")) {
                                revs->unpacked = 1;
                                add_ignore_packed(revs, arg+11);
                                continue;
                                revs->verbose_header = 1;
                                continue;
                        }
 -                      if (!strncmp(arg, "--pretty", 8)) {
 +                      if (!prefixcmp(arg, "--pretty")) {
                                revs->verbose_header = 1;
                                revs->commit_format = get_commit_format(arg+8);
                                continue;
                                revs->abbrev = DEFAULT_ABBREV;
                                continue;
                        }
 -                      if (!strncmp(arg, "--abbrev=", 9)) {
 +                      if (!prefixcmp(arg, "--abbrev=")) {
                                revs->abbrev = strtoul(arg + 9, NULL, 10);
                                if (revs->abbrev < MINIMUM_ABBREV)
                                        revs->abbrev = MINIMUM_ABBREV;
                        /*
                         * Grepping the commit log
                         */
 -                      if (!strncmp(arg, "--author=", 9)) {
 +                      if (!prefixcmp(arg, "--author=")) {
                                add_header_grep(revs, "author", arg+9);
                                continue;
                        }
 -                      if (!strncmp(arg, "--committer=", 12)) {
 +                      if (!prefixcmp(arg, "--committer=")) {
                                add_header_grep(revs, "committer", arg+12);
                                continue;
                        }
 -                      if (!strncmp(arg, "--grep=", 7)) {
 +                      if (!prefixcmp(arg, "--grep=")) {
                                add_message_grep(revs, arg+7);
                                continue;
                        }
                                all_match = 1;
                                continue;
                        }
 -                      if (!strncmp(arg, "--encoding=", 11)) {
 +                      if (!prefixcmp(arg, "--encoding=")) {
                                arg += 11;
                                if (strcmp(arg, "none"))
                                        git_log_output_encoding = strdup(arg);
                                        git_log_output_encoding = "";
                                continue;
                        }
 +                      if (!strcmp(arg, "--reverse")) {
 +                              revs->reverse ^= 1;
 +                              continue;
 +                      }
  
                        opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
                        if (opts > 0) {
@@@ -1235,15 -1229,9 +1235,15 @@@ static struct commit *get_revision_1(st
                 */
                if (!revs->limited) {
                        if (revs->max_age != -1 &&
 -                          (commit->date < revs->max_age))
 -                              continue;
 -                      add_parents_to_list(revs, commit, &revs->commits);
 +                          (commit->date < revs->max_age)) {
 +                              if (revs->boundary)
 +                                      commit->object.flags |=
 +                                              BOUNDARY_SHOW | BOUNDARY;
 +                              else
 +                                      continue;
 +                      } else
 +                              add_parents_to_list(revs, commit,
 +                                              &revs->commits);
                }
                if (commit->object.flags & SHOWN)
                        continue;
@@@ -1298,40 -1286,6 +1298,40 @@@ struct commit *get_revision(struct rev_
  {
        struct commit *c = NULL;
  
 +      if (revs->reverse) {
 +              struct commit_list *list;
 +
 +              /*
 +               * rev_info.reverse is used to note the fact that we
 +               * want to output the list of revisions in reverse
 +               * order.  To accomplish this goal, reverse can have
 +               * different values:
 +               *
 +               *  0  do nothing
 +               *  1  reverse the list
 +               *  2  internal use:  we have already obtained and
 +               *     reversed the list, now we only need to yield
 +               *     its items.
 +               */
 +
 +              if (revs->reverse == 1) {
 +                      revs->reverse = 0;
 +                      list = NULL;
 +                      while ((c = get_revision(revs)))
 +                              commit_list_insert(c, &list);
 +                      revs->commits = list;
 +                      revs->reverse = 2;
 +              }
 +
 +              if (!revs->commits)
 +                      return NULL;
 +              c = revs->commits->item;
 +              list = revs->commits->next;
 +              free(revs->commits);
 +              revs->commits = list;
 +              return c;
 +      }
 +
        if (0 < revs->skip_count) {
                while ((c = get_revision_1(revs)) != NULL) {
                        if (revs->skip_count-- <= 0)
        case -1:
                break;
        case 0:
 -              return NULL;
 +              if (revs->boundary) {
 +                      struct commit_list *list = revs->commits;
 +                      while (list) {
 +                              list->item->object.flags |=
 +                                      BOUNDARY_SHOW | BOUNDARY;
 +                              list = list->next;
 +                      }
 +                      /* all remaining commits are boundary commits */
 +                      revs->max_count = -1;
 +                      revs->limited = 1;
 +              } else
 +                      return NULL;
        default:
                revs->max_count--;
        }