Merge branch 'maint-1.5.4' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 16 Apr 2008 07:37:33 +0000 (00:37 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Apr 2008 07:37:33 +0000 (00:37 -0700)
* maint-1.5.4:
git-bisect: make "start", "good" and "skip" succeed or fail atomically
git-am: cope better with an empty Subject: line
Ignore leading empty lines while summarizing merges
bisect: squelch "fatal: ref HEAD not a symref" misleading message
builtin-apply: Show a more descriptive error on failure when opening a patch
Clarify documentation of git-cvsserver, particularly in relation to git-shell

1  2 
Documentation/git-cvsserver.txt
builtin-apply.c
builtin-fmt-merge-msg.c
git-am.sh
git-bisect.sh
t/t6030-bisect-porcelain.sh
index 9cec8021b8c66b6bac692e2a9f14204503f7f18a,0b6db864fa328bec4899154e5455debae6e02ddc..b1106714b2f7100124826819315e78f7f28325fb
@@@ -110,7 -110,9 +110,9 @@@ cvs -d ":ext;CVS_SERVER=git-cvsserver:u
  ------
  This has the advantage that it will be saved in your 'CVS/Root' files and
  you don't need to worry about always setting the correct environment
- variable.
+ variable.  SSH users restricted to git-shell don't need to override the default
+ with CVS_SERVER (and shouldn't) as git-shell understands `cvs` to mean
+ git-cvsserver and pretends that the other end runs the real cvs better.
  --
  2. For each repo that you want accessible from CVS you need to edit config in
     the repo and add the following section.
@@@ -141,25 -143,29 +143,29 @@@ allowing access over SSH
          enabled=1
  ------
  --
- 3. On the client machine you need to set the following variables.
-    CVSROOT should be set as per normal, but the directory should point at the
-    appropriate git repo. For example:
+ 3. If you didn't specify the CVSROOT/CVS_SERVER directly in the checkout command,
+    automatically saving it in your 'CVS/Root' files, then you need to set them
+    explicitly in your environment.  CVSROOT should be set as per normal, but the
+    directory should point at the appropriate git repo.  As above, for SSH clients
+    _not_ restricted to git-shell, CVS_SERVER should be set to git-cvsserver.
  +
  --
- For SSH access, CVS_SERVER should be set to git-cvsserver
- Example:
  ------
       export CVSROOT=:ext:user@server:/var/git/project.git
       export CVS_SERVER=git-cvsserver
  ------
  --
- 4. For SSH clients that will make commits, make sure their .bashrc file
-    sets the GIT_AUTHOR and GIT_COMMITTER variables.
+ 4. For SSH clients that will make commits, make sure their server-side
+    .ssh/environment files (or .bashrc, etc., according to their specific shell)
+    export appropriate values for GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL,
+    GIT_COMMITTER_NAME, and GIT_COMMITTER_EMAIL.  For SSH clients whose login
+    shell is bash, .bashrc may be a reasonable alternative.
  
  5. Clients should now be able to check out the project. Use the CVS 'module'
-    name to indicate what GIT 'head' you want to check out. Example:
+    name to indicate what GIT 'head' you want to check out.  This also sets the
+    name of your newly checked-out directory, unless you tell it otherwise with
+    `-d <dir_name>`.  For example, this checks out 'master' branch to the
+    `project-master` directory:
  +
  ------
       cvs co -d project-master master
@@@ -227,11 -233,6 +233,11 @@@ gitcvs.dbpass:
        Database password.  Only useful if setting `dbdriver`, since
        SQLite has no concept of database passwords.
  
 +gitcvs.dbTableNamePrefix::
 +      Database table name prefix.  Supports variable substitution
 +      (see below).  Any non-alphabetic characters will be replaced
 +      with underscores.
 +
  All variables can also be set per access method, see <<configaccessmethod,above>>.
  
  Variable substitution
diff --combined builtin-apply.c
index abe73a0f8194e95c2a10a27c2007560e6d57f3d5,65388353a75814d940171aa259e4058683f355a2..30d26e57b30821d6b11efe4f493da6d35fb1dc18
@@@ -161,84 -161,6 +161,84 @@@ struct patch 
        struct patch *next;
  };
  
 +/*
 + * A line in a file, len-bytes long (includes the terminating LF,
 + * except for an incomplete line at the end if the file ends with
 + * one), and its contents hashes to 'hash'.
 + */
 +struct line {
 +      size_t len;
 +      unsigned hash : 24;
 +      unsigned flag : 8;
 +#define LINE_COMMON     1
 +};
 +
 +/*
 + * This represents a "file", which is an array of "lines".
 + */
 +struct image {
 +      char *buf;
 +      size_t len;
 +      size_t nr;
 +      size_t alloc;
 +      struct line *line_allocated;
 +      struct line *line;
 +};
 +
 +static uint32_t hash_line(const char *cp, size_t len)
 +{
 +      size_t i;
 +      uint32_t h;
 +      for (i = 0, h = 0; i < len; i++) {
 +              if (!isspace(cp[i])) {
 +                      h = h * 3 + (cp[i] & 0xff);
 +              }
 +      }
 +      return h;
 +}
 +
 +static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
 +{
 +      ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
 +      img->line_allocated[img->nr].len = len;
 +      img->line_allocated[img->nr].hash = hash_line(bol, len);
 +      img->line_allocated[img->nr].flag = flag;
 +      img->nr++;
 +}
 +
 +static void prepare_image(struct image *image, char *buf, size_t len,
 +                        int prepare_linetable)
 +{
 +      const char *cp, *ep;
 +
 +      memset(image, 0, sizeof(*image));
 +      image->buf = buf;
 +      image->len = len;
 +
 +      if (!prepare_linetable)
 +              return;
 +
 +      ep = image->buf + image->len;
 +      cp = image->buf;
 +      while (cp < ep) {
 +              const char *next;
 +              for (next = cp; next < ep && *next != '\n'; next++)
 +                      ;
 +              if (next < ep)
 +                      next++;
 +              add_line_info(image, cp, next - cp, 0);
 +              cp = next;
 +      }
 +      image->line = image->line_allocated;
 +}
 +
 +static void clear_image(struct image *image)
 +{
 +      free(image->buf);
 +      image->buf = NULL;
 +      image->len = 0;
 +}
 +
  static void say_patch_name(FILE *output, const char *pre,
                           struct patch *patch, const char *post)
  {
@@@ -1508,345 -1430,234 +1508,345 @@@ static int read_old_data(struct stat *s
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
                        return error("unable to open or read %s", path);
 -              convert_to_git(path, buf->buf, buf->len, buf);
 +              convert_to_git(path, buf->buf, buf->len, buf, 0);
                return 0;
        default:
                return -1;
        }
  }
  
 -static int find_offset(const char *buf, unsigned long size,
 -                     const char *fragment, unsigned long fragsize,
 -                     int line, int *lines)
 +static void update_pre_post_images(struct image *preimage,
 +                                 struct image *postimage,
 +                                 char *buf,
 +                                 size_t len)
  {
 -      int i;
 -      unsigned long start, backwards, forwards;
 +      int i, ctx;
 +      char *new, *old, *fixed;
 +      struct image fixed_preimage;
  
 -      if (fragsize > size)
 -              return -1;
 +      /*
 +       * Update the preimage with whitespace fixes.  Note that we
 +       * are not losing preimage->buf -- apply_one_fragment() will
 +       * free "oldlines".
 +       */
 +      prepare_image(&fixed_preimage, buf, len, 1);
 +      assert(fixed_preimage.nr == preimage->nr);
 +      for (i = 0; i < preimage->nr; i++)
 +              fixed_preimage.line[i].flag = preimage->line[i].flag;
 +      free(preimage->line_allocated);
 +      *preimage = fixed_preimage;
  
 -      start = 0;
 -      if (line > 1) {
 -              unsigned long offset = 0;
 -              i = line-1;
 -              while (offset + fragsize <= size) {
 -                      if (buf[offset++] == '\n') {
 -                              start = offset;
 -                              if (!--i)
 -                                      break;
 -                      }
 +      /*
 +       * Adjust the common context lines in postimage, in place.
 +       * This is possible because whitespace fixing does not make
 +       * the string grow.
 +       */
 +      new = old = postimage->buf;
 +      fixed = preimage->buf;
 +      for (i = ctx = 0; i < postimage->nr; i++) {
 +              size_t len = postimage->line[i].len;
 +              if (!(postimage->line[i].flag & LINE_COMMON)) {
 +                      /* an added line -- no counterparts in preimage */
 +                      memmove(new, old, len);
 +                      old += len;
 +                      new += len;
 +                      continue;
                }
 +
 +              /* a common context -- skip it in the original postimage */
 +              old += len;
 +
 +              /* and find the corresponding one in the fixed preimage */
 +              while (ctx < preimage->nr &&
 +                     !(preimage->line[ctx].flag & LINE_COMMON)) {
 +                      fixed += preimage->line[ctx].len;
 +                      ctx++;
 +              }
 +              if (preimage->nr <= ctx)
 +                      die("oops");
 +
 +              /* and copy it in, while fixing the line length */
 +              len = preimage->line[ctx].len;
 +              memcpy(new, fixed, len);
 +              new += len;
 +              fixed += len;
 +              postimage->line[i].len = len;
 +              ctx++;
        }
  
 -      /* Exact line number? */
 -      if ((start + fragsize <= size) &&
 -          !memcmp(buf + start, fragment, fragsize))
 -              return start;
 +      /* Fix the length of the whole thing */
 +      postimage->len = new - postimage->buf;
 +}
 +
 +static int match_fragment(struct image *img,
 +                        struct image *preimage,
 +                        struct image *postimage,
 +                        unsigned long try,
 +                        int try_lno,
 +                        unsigned ws_rule,
 +                        int match_beginning, int match_end)
 +{
 +      int i;
 +      char *fixed_buf, *buf, *orig, *target;
 +
 +      if (preimage->nr + try_lno > img->nr)
 +              return 0;
 +
 +      if (match_beginning && try_lno)
 +              return 0;
 +
 +      if (match_end && preimage->nr + try_lno != img->nr)
 +              return 0;
 +
 +      /* Quick hash check */
 +      for (i = 0; i < preimage->nr; i++)
 +              if (preimage->line[i].hash != img->line[try_lno + i].hash)
 +                      return 0;
 +
 +      /*
 +       * Do we have an exact match?  If we were told to match
 +       * at the end, size must be exactly at try+fragsize,
 +       * otherwise try+fragsize must be still within the preimage,
 +       * and either case, the old piece should match the preimage
 +       * exactly.
 +       */
 +      if ((match_end
 +           ? (try + preimage->len == img->len)
 +           : (try + preimage->len <= img->len)) &&
 +          !memcmp(img->buf + try, preimage->buf, preimage->len))
 +              return 1;
 +
 +      if (ws_error_action != correct_ws_error)
 +              return 0;
 +
 +      /*
 +       * The hunk does not apply byte-by-byte, but the hash says
 +       * it might with whitespace fuzz.
 +       */
 +      fixed_buf = xmalloc(preimage->len + 1);
 +      buf = fixed_buf;
 +      orig = preimage->buf;
 +      target = img->buf + try;
 +      for (i = 0; i < preimage->nr; i++) {
 +              size_t fixlen; /* length after fixing the preimage */
 +              size_t oldlen = preimage->line[i].len;
 +              size_t tgtlen = img->line[try_lno + i].len;
 +              size_t tgtfixlen; /* length after fixing the target line */
 +              char tgtfixbuf[1024], *tgtfix;
 +              int match;
 +
 +              /* Try fixing the line in the preimage */
 +              fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
 +
 +              /* Try fixing the line in the target */
 +              if (sizeof(tgtfixbuf) < tgtlen)
 +                      tgtfix = tgtfixbuf;
 +              else
 +                      tgtfix = xmalloc(tgtlen);
 +              tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
 +
 +              /*
 +               * If they match, either the preimage was based on
 +               * a version before our tree fixed whitespace breakage,
 +               * or we are lacking a whitespace-fix patch the tree
 +               * the preimage was based on already had (i.e. target
 +               * has whitespace breakage, the preimage doesn't).
 +               * In either case, we are fixing the whitespace breakages
 +               * so we might as well take the fix together with their
 +               * real change.
 +               */
 +              match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
 +
 +              if (tgtfix != tgtfixbuf)
 +                      free(tgtfix);
 +              if (!match)
 +                      goto unmatch_exit;
 +
 +              orig += oldlen;
 +              buf += fixlen;
 +              target += tgtlen;
 +      }
 +
 +      /*
 +       * Yes, the preimage is based on an older version that still
 +       * has whitespace breakages unfixed, and fixing them makes the
 +       * hunk match.  Update the context lines in the postimage.
 +       */
 +      update_pre_post_images(preimage, postimage,
 +                             fixed_buf, buf - fixed_buf);
 +      return 1;
 +
 + unmatch_exit:
 +      free(fixed_buf);
 +      return 0;
 +}
 +
 +static int find_pos(struct image *img,
 +                  struct image *preimage,
 +                  struct image *postimage,
 +                  int line,
 +                  unsigned ws_rule,
 +                  int match_beginning, int match_end)
 +{
 +      int i;
 +      unsigned long backwards, forwards, try;
 +      int backwards_lno, forwards_lno, try_lno;
 +
 +      if (preimage->nr > img->nr)
 +              return -1;
 +
 +      /*
 +       * If match_begining or match_end is specified, there is no
 +       * point starting from a wrong line that will never match and
 +       * wander around and wait for a match at the specified end.
 +       */
 +      if (match_beginning)
 +              line = 0;
 +      else if (match_end)
 +              line = img->nr - preimage->nr;
 +
 +      if (line > img->nr)
 +              line = img->nr;
 +
 +      try = 0;
 +      for (i = 0; i < line; i++)
 +              try += img->line[i].len;
  
        /*
         * There's probably some smart way to do this, but I'll leave
         * that to the smart and beautiful people. I'm simple and stupid.
         */
 -      backwards = start;
 -      forwards = start;
 +      backwards = try;
 +      backwards_lno = line;
 +      forwards = try;
 +      forwards_lno = line;
 +      try_lno = line;
 +
        for (i = 0; ; i++) {
 -              unsigned long try;
 -              int n;
 +              if (match_fragment(img, preimage, postimage,
 +                                 try, try_lno, ws_rule,
 +                                 match_beginning, match_end))
 +                      return try_lno;
 +
 +      again:
 +              if (backwards_lno == 0 && forwards_lno == img->nr)
 +                      break;
  
 -              /* "backward" */
                if (i & 1) {
 -                      if (!backwards) {
 -                              if (forwards + fragsize > size)
 -                                      break;
 -                              continue;
 +                      if (backwards_lno == 0) {
 +                              i++;
 +                              goto again;
                        }
 -                      do {
 -                              --backwards;
 -                      } while (backwards && buf[backwards-1] != '\n');
 +                      backwards_lno--;
 +                      backwards -= img->line[backwards_lno].len;
                        try = backwards;
 +                      try_lno = backwards_lno;
                } else {
 -                      while (forwards + fragsize <= size) {
 -                              if (buf[forwards++] == '\n')
 -                                      break;
 +                      if (forwards_lno == img->nr) {
 +                              i++;
 +                              goto again;
                        }
 +                      forwards += img->line[forwards_lno].len;
 +                      forwards_lno++;
                        try = forwards;
 +                      try_lno = forwards_lno;
                }
  
 -              if (try + fragsize > size)
 -                      continue;
 -              if (memcmp(buf + try, fragment, fragsize))
 -                      continue;
 -              n = (i >> 1)+1;
 -              if (i & 1)
 -                      n = -n;
 -              *lines = n;
 -              return try;
        }
 -
 -      /*
 -       * We should start searching forward and backward.
 -       */
        return -1;
  }
  
 -static void remove_first_line(const char **rbuf, int *rsize)
 +static void remove_first_line(struct image *img)
  {
 -      const char *buf = *rbuf;
 -      int size = *rsize;
 -      unsigned long offset;
 -      offset = 0;
 -      while (offset <= size) {
 -              if (buf[offset++] == '\n')
 -                      break;
 -      }
 -      *rsize = size - offset;
 -      *rbuf = buf + offset;
 +      img->buf += img->line[0].len;
 +      img->len -= img->line[0].len;
 +      img->line++;
 +      img->nr--;
  }
  
 -static void remove_last_line(const char **rbuf, int *rsize)
 +static void remove_last_line(struct image *img)
  {
 -      const char *buf = *rbuf;
 -      int size = *rsize;
 -      unsigned long offset;
 -      offset = size - 1;
 -      while (offset > 0) {
 -              if (buf[--offset] == '\n')
 -                      break;
 -      }
 -      *rsize = offset + 1;
 +      img->len -= img->line[--img->nr].len;
  }
  
 -static int apply_line(char *output, const char *patch, int plen,
 -                    unsigned ws_rule)
 +static void update_image(struct image *img,
 +                       int applied_pos,
 +                       struct image *preimage,
 +                       struct image *postimage)
  {
        /*
 -       * plen is number of bytes to be copied from patch,
 -       * starting at patch+1 (patch[0] is '+').  Typically
 -       * patch[plen] is '\n', unless this is the incomplete
 -       * last line.
 +       * remove the copy of preimage at offset in img
 +       * and replace it with postimage
         */
 -      int i;
 -      int add_nl_to_tail = 0;
 -      int fixed = 0;
 -      int last_tab_in_indent = 0;
 -      int last_space_in_indent = 0;
 -      int need_fix_leading_space = 0;
 -      char *buf;
 -
 -      if ((ws_error_action != correct_ws_error) || !whitespace_error ||
 -          *patch != '+') {
 -              memcpy(output, patch + 1, plen);
 -              return plen;
 -      }
 -
 -      /*
 -       * Strip trailing whitespace
 -       */
 -      if ((ws_rule & WS_TRAILING_SPACE) &&
 -          (1 < plen && isspace(patch[plen-1]))) {
 -              if (patch[plen] == '\n')
 -                      add_nl_to_tail = 1;
 -              plen--;
 -              while (0 < plen && isspace(patch[plen]))
 -                      plen--;
 -              fixed = 1;
 -      }
 -
 -      /*
 -       * Check leading whitespaces (indent)
 -       */
 -      for (i = 1; i < plen; i++) {
 -              char ch = patch[i];
 -              if (ch == '\t') {
 -                      last_tab_in_indent = i;
 -                      if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
 -                          0 < last_space_in_indent)
 -                          need_fix_leading_space = 1;
 -              } else if (ch == ' ') {
 -                      last_space_in_indent = i;
 -                      if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
 -                          8 <= i - last_tab_in_indent)
 -                              need_fix_leading_space = 1;
 -              }
 -              else
 -                      break;
 -      }
 -
 -      buf = output;
 -      if (need_fix_leading_space) {
 -              int consecutive_spaces = 0;
 -              int last = last_tab_in_indent + 1;
 -
 -              if (ws_rule & WS_INDENT_WITH_NON_TAB) {
 -                      /* have "last" point at one past the indent */
 -                      if (last_tab_in_indent < last_space_in_indent)
 -                              last = last_space_in_indent + 1;
 -                      else
 -                              last = last_tab_in_indent + 1;
 -              }
 +      int i, nr;
 +      size_t remove_count, insert_count, applied_at = 0;
 +      char *result;
  
 +      for (i = 0; i < applied_pos; i++)
 +              applied_at += img->line[i].len;
 +
 +      remove_count = 0;
 +      for (i = 0; i < preimage->nr; i++)
 +              remove_count += img->line[applied_pos + i].len;
 +      insert_count = postimage->len;
 +
 +      /* Adjust the contents */
 +      result = xmalloc(img->len + insert_count - remove_count + 1);
 +      memcpy(result, img->buf, applied_at);
 +      memcpy(result + applied_at, postimage->buf, postimage->len);
 +      memcpy(result + applied_at + postimage->len,
 +             img->buf + (applied_at + remove_count),
 +             img->len - (applied_at + remove_count));
 +      free(img->buf);
 +      img->buf = result;
 +      img->len += insert_count - remove_count;
 +      result[img->len] = '\0';
 +
 +      /* Adjust the line table */
 +      nr = img->nr + postimage->nr - preimage->nr;
 +      if (preimage->nr < postimage->nr) {
                /*
 -               * between patch[1..last], strip the funny spaces,
 -               * updating them to tab as needed.
 +               * NOTE: this knows that we never call remove_first_line()
 +               * on anything other than pre/post image.
                 */
 -              for (i = 1; i < last; i++, plen--) {
 -                      char ch = patch[i];
 -                      if (ch != ' ') {
 -                              consecutive_spaces = 0;
 -                              *output++ = ch;
 -                      } else {
 -                              consecutive_spaces++;
 -                              if (consecutive_spaces == 8) {
 -                                      *output++ = '\t';
 -                                      consecutive_spaces = 0;
 -                              }
 -                      }
 -              }
 -              while (0 < consecutive_spaces--)
 -                      *output++ = ' ';
 -              fixed = 1;
 -              i = last;
 +              img->line = xrealloc(img->line, nr * sizeof(*img->line));
 +              img->line_allocated = img->line;
        }
 -      else
 -              i = 1;
 -
 -      memcpy(output, patch + i, plen);
 -      if (add_nl_to_tail)
 -              output[plen++] = '\n';
 -      if (fixed)
 -              applied_after_fixing_ws++;
 -      return output + plen - buf;
 +      if (preimage->nr != postimage->nr)
 +              memmove(img->line + applied_pos + postimage->nr,
 +                      img->line + applied_pos + preimage->nr,
 +                      (img->nr - (applied_pos + preimage->nr)) *
 +                      sizeof(*img->line));
 +      memcpy(img->line + applied_pos,
 +             postimage->line,
 +             postimage->nr * sizeof(*img->line));
 +      img->nr = nr;
  }
  
 -static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
 +static int apply_one_fragment(struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule)
  {
        int match_beginning, match_end;
        const char *patch = frag->patch;
 -      int offset, size = frag->size;
 -      char *old = xmalloc(size);
 -      char *new = xmalloc(size);
 -      const char *oldlines, *newlines;
 -      int oldsize = 0, newsize = 0;
 +      int size = frag->size;
 +      char *old, *new, *oldlines, *newlines;
        int new_blank_lines_at_end = 0;
        unsigned long leading, trailing;
 -      int pos, lines;
 +      int pos, applied_pos;
 +      struct image preimage;
 +      struct image postimage;
 +
 +      memset(&preimage, 0, sizeof(preimage));
 +      memset(&postimage, 0, sizeof(postimage));
 +      oldlines = xmalloc(size);
 +      newlines = xmalloc(size);
  
 +      old = oldlines;
 +      new = newlines;
        while (size > 0) {
                char first;
                int len = linelen(patch, size);
 -              int plen;
 +              int plen, added;
                int added_blank_line = 0;
  
                if (!len)
                 * followed by "\ No newline", then we also remove the
                 * last one (which is the newline, of course).
                 */
 -              plen = len-1;
 +              plen = len - 1;
                if (len < size && patch[len] == '\\')
                        plen--;
                first = *patch;
                        if (plen < 0)
                                /* ... followed by '\No newline'; nothing */
                                break;
 -                      old[oldsize++] = '\n';
 -                      new[newsize++] = '\n';
 +                      *old++ = '\n';
 +                      *new++ = '\n';
 +                      add_line_info(&preimage, "\n", 1, LINE_COMMON);
 +                      add_line_info(&postimage, "\n", 1, LINE_COMMON);
                        break;
                case ' ':
                case '-':
 -                      memcpy(old + oldsize, patch + 1, plen);
 -                      oldsize += plen;
 +                      memcpy(old, patch + 1, plen);
 +                      add_line_info(&preimage, old, plen,
 +                                    (first == ' ' ? LINE_COMMON : 0));
 +                      old += plen;
                        if (first == '-')
                                break;
                /* Fall-through for ' ' */
                case '+':
 -                      if (first != '+' || !no_add) {
 -                              int added = apply_line(new + newsize, patch,
 -                                                     plen, ws_rule);
 -                              newsize += added;
 -                              if (first == '+' &&
 -                                  added == 1 && new[newsize-1] == '\n')
 -                                      added_blank_line = 1;
 +                      /* --no-add does not add new lines */
 +                      if (first == '+' && no_add)
 +                              break;
 +
 +                      if (first != '+' ||
 +                          !whitespace_error ||
 +                          ws_error_action != correct_ws_error) {
 +                              memcpy(new, patch + 1, plen);
 +                              added = plen;
                        }
 +                      else {
 +                              added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
 +                      }
 +                      add_line_info(&postimage, new, added,
 +                                    (first == '+' ? 0 : LINE_COMMON));
 +                      new += added;
 +                      if (first == '+' &&
 +                          added == 1 && new[-1] == '\n')
 +                              added_blank_line = 1;
                        break;
                case '@': case '\\':
                        /* Ignore it, we already handled it */
                patch += len;
                size -= len;
        }
 -
        if (inaccurate_eof &&
 -          oldsize > 0 && old[oldsize - 1] == '\n' &&
 -          newsize > 0 && new[newsize - 1] == '\n') {
 -              oldsize--;
 -              newsize--;
 +          old > oldlines && old[-1] == '\n' &&
 +          new > newlines && new[-1] == '\n') {
 +              old--;
 +              new--;
        }
  
 -      oldlines = old;
 -      newlines = new;
        leading = frag->leading;
        trailing = frag->trailing;
  
         */
        match_end = !unidiff_zero && !trailing;
  
 -      lines = 0;
 -      pos = frag->newpos;
 +      pos = frag->newpos ? (frag->newpos - 1) : 0;
 +      preimage.buf = oldlines;
 +      preimage.len = old - oldlines;
 +      postimage.buf = newlines;
 +      postimage.len = new - newlines;
 +      preimage.line = preimage.line_allocated;
 +      postimage.line = postimage.line_allocated;
 +
        for (;;) {
 -              offset = find_offset(buf->buf, buf->len,
 -                                   oldlines, oldsize, pos, &lines);
 -              if (match_end && offset + oldsize != buf->len)
 -                      offset = -1;
 -              if (match_beginning && offset)
 -                      offset = -1;
 -              if (offset >= 0) {
 -                      if (ws_error_action == correct_ws_error &&
 -                          (buf->len - oldsize - offset == 0)) /* end of file? */
 -                              newsize -= new_blank_lines_at_end;
 -
 -                      /* Warn if it was necessary to reduce the number
 -                       * of context lines.
 -                       */
 -                      if ((leading != frag->leading) ||
 -                          (trailing != frag->trailing))
 -                              fprintf(stderr, "Context reduced to (%ld/%ld)"
 -                                      " to apply fragment at %d\n",
 -                                      leading, trailing, pos + lines);
 -
 -                      strbuf_splice(buf, offset, oldsize, newlines, newsize);
 -                      offset = 0;
 +
 +              applied_pos = find_pos(img, &preimage, &postimage, pos,
 +                                     ws_rule, match_beginning, match_end);
 +
 +              if (applied_pos >= 0)
                        break;
 -              }
  
                /* Am I at my context limits? */
                if ((leading <= p_context) && (trailing <= p_context))
                        match_beginning = match_end = 0;
                        continue;
                }
 +
                /*
                 * Reduce the number of context lines; reduce both
                 * leading and trailing if they are equal otherwise
                 * just reduce the larger context.
                 */
                if (leading >= trailing) {
 -                      remove_first_line(&oldlines, &oldsize);
 -                      remove_first_line(&newlines, &newsize);
 +                      remove_first_line(&preimage);
 +                      remove_first_line(&postimage);
                        pos--;
                        leading--;
                }
                if (trailing > leading) {
 -                      remove_last_line(&oldlines, &oldsize);
 -                      remove_last_line(&newlines, &newsize);
 +                      remove_last_line(&preimage);
 +                      remove_last_line(&postimage);
                        trailing--;
                }
        }
  
 -      if (offset && apply_verbosely)
 -              error("while searching for:\n%.*s", oldsize, oldlines);
 +      if (applied_pos >= 0) {
 +              if (ws_error_action == correct_ws_error &&
 +                  new_blank_lines_at_end &&
 +                  postimage.nr + applied_pos == img->nr) {
 +                      /*
 +                       * If the patch application adds blank lines
 +                       * at the end, and if the patch applies at the
 +                       * end of the image, remove those added blank
 +                       * lines.
 +                       */
 +                      while (new_blank_lines_at_end--)
 +                              remove_last_line(&postimage);
 +              }
  
 -      free(old);
 -      free(new);
 -      return offset;
 +              /*
 +               * Warn if it was necessary to reduce the number
 +               * of context lines.
 +               */
 +              if ((leading != frag->leading) ||
 +                  (trailing != frag->trailing))
 +                      fprintf(stderr, "Context reduced to (%ld/%ld)"
 +                              " to apply fragment at %d\n",
 +                              leading, trailing, applied_pos+1);
 +              update_image(img, applied_pos, &preimage, &postimage);
 +      } else {
 +              if (apply_verbosely)
 +                      error("while searching for:\n%.*s",
 +                            (int)(old - oldlines), oldlines);
 +      }
 +
 +      free(oldlines);
 +      free(newlines);
 +      free(preimage.line_allocated);
 +      free(postimage.line_allocated);
 +
 +      return (applied_pos < 0);
  }
  
 -static int apply_binary_fragment(struct strbuf *buf, struct patch *patch)
 +static int apply_binary_fragment(struct image *img, struct patch *patch)
  {
        struct fragment *fragment = patch->fragments;
        unsigned long len;
        }
        switch (fragment->binary_patch_method) {
        case BINARY_DELTA_DEFLATED:
 -              dst = patch_delta(buf->buf, buf->len, fragment->patch,
 +              dst = patch_delta(img->buf, img->len, fragment->patch,
                                  fragment->size, &len);
                if (!dst)
                        return -1;
 -              /* XXX patch_delta NUL-terminates */
 -              strbuf_attach(buf, dst, len, len + 1);
 +              clear_image(img);
 +              img->buf = dst;
 +              img->len = len;
                return 0;
        case BINARY_LITERAL_DEFLATED:
 -              strbuf_reset(buf);
 -              strbuf_add(buf, fragment->patch, fragment->size);
 +              clear_image(img);
 +              img->len = fragment->size;
 +              img->buf = xmalloc(img->len+1);
 +              memcpy(img->buf, fragment->patch, img->len);
 +              img->buf[img->len] = '\0';
                return 0;
        }
        return -1;
  }
  
 -static int apply_binary(struct strbuf *buf, struct patch *patch)
 +static int apply_binary(struct image *img, struct patch *patch)
  {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
                 * See if the old one matches what the patch
                 * applies to.
                 */
 -              hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
 +              hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
                        return error("the patch applies to '%s' (%s), "
                                     "which does not match the "
        }
        else {
                /* Otherwise, the old one must be empty. */
 -              if (buf->len)
 +              if (img->len)
                        return error("the patch applies to an empty "
                                     "'%s' but it is not empty", name);
        }
  
        get_sha1_hex(patch->new_sha1_prefix, sha1);
        if (is_null_sha1(sha1)) {
 -              strbuf_release(buf);
 +              clear_image(img);
                return 0; /* deletion patch */
        }
  
                        return error("the necessary postimage %s for "
                                     "'%s' cannot be read",
                                     patch->new_sha1_prefix, name);
 -              /* XXX read_sha1_file NUL-terminates */
 -              strbuf_attach(buf, result, size, size + 1);
 +              clear_image(img);
 +              img->buf = result;
 +              img->len = size;
        } else {
                /*
                 * We have verified buf matches the preimage;
                 * apply the patch data to it, which is stored
                 * in the patch->fragments->{patch,size}.
                 */
 -              if (apply_binary_fragment(buf, patch))
 +              if (apply_binary_fragment(img, patch))
                        return error("binary patch does not apply to '%s'",
                                     name);
  
                /* verify that the result matches */
 -              hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
 +              hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
                        return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
                                name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        return 0;
  }
  
 -static int apply_fragments(struct strbuf *buf, struct patch *patch)
 +static int apply_fragments(struct image *img, struct patch *patch)
  {
        struct fragment *frag = patch->fragments;
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned inaccurate_eof = patch->inaccurate_eof;
  
        if (patch->is_binary)
 -              return apply_binary(buf, patch);
 +              return apply_binary(img, patch);
  
        while (frag) {
 -              if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
 +              if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule)) {
                        error("patch failed: %s:%ld", name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
@@@ -2174,7 -1949,7 +2174,7 @@@ static int read_file_or_gitlink(struct 
        if (!ce)
                return 0;
  
 -      if (S_ISGITLINK(ntohl(ce->ce_mode))) {
 +      if (S_ISGITLINK(ce->ce_mode)) {
                strbuf_grow(buf, 100);
                strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
        } else {
  static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
  {
        struct strbuf buf;
 +      struct image image;
 +      size_t len;
 +      char *img;
  
        strbuf_init(&buf, 0);
        if (cached) {
                }
        }
  
 -      if (apply_fragments(&buf, patch) < 0)
 +      img = strbuf_detach(&buf, &len);
 +      prepare_image(&image, img, len, !patch->is_binary);
 +
 +      if (apply_fragments(&image, patch) < 0)
                return -1; /* note with --reject this succeeds. */
 -      patch->result = strbuf_detach(&buf, &patch->resultsize);
 +      patch->result = image.buf;
 +      patch->resultsize = image.len;
 +      free(image.line_allocated);
  
        if (0 < patch->is_delete && patch->resultsize)
                return error("removal patch leaves file contents");
@@@ -2259,7 -2026,7 +2259,7 @@@ static int check_to_create_blob(const c
  
  static int verify_index_match(struct cache_entry *ce, struct stat *st)
  {
 -      if (S_ISGITLINK(ntohl(ce->ce_mode))) {
 +      if (S_ISGITLINK(ce->ce_mode)) {
                if (!S_ISDIR(st->st_mode))
                        return -1;
                return 0;
@@@ -2318,12 -2085,12 +2318,12 @@@ static int check_patch(struct patch *pa
                                return error("%s: does not match index",
                                             old_name);
                        if (cached)
 -                              st_mode = ntohl(ce->ce_mode);
 +                              st_mode = ce->ce_mode;
                } else if (stat_ret < 0)
                        return error("%s: %s", old_name, strerror(errno));
  
                if (!cached)
 -                      st_mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
 +                      st_mode = ce_mode_from_stat(ce, st.st_mode);
  
                if (patch->is_new < 0)
                        patch->is_new = 0;
@@@ -2624,7 -2391,7 +2624,7 @@@ static void add_index_file(const char *
        ce = xcalloc(1, ce_size);
        memcpy(ce->name, path, namelen);
        ce->ce_mode = create_ce_mode(mode);
 -      ce->ce_flags = htons(namelen);
 +      ce->ce_flags = namelen;
        if (S_ISGITLINK(mode)) {
                const char *s = buf;
  
@@@ -2997,7 -2764,7 +2997,7 @@@ int cmd_apply(int argc, const char **ar
        int read_stdin = 1;
        int inaccurate_eof = 0;
        int errs = 0;
 -      int is_not_gitdir = 0;
 +      int is_not_gitdir;
  
        const char *whitespace_option = NULL;
  
  
                fd = open(arg, O_RDONLY);
                if (fd < 0)
-                       usage(apply_usage);
+                       die("can't open patch '%s': %s", arg, strerror(errno));
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
                errs |= apply_patch(fd, arg, inaccurate_eof);
diff --combined builtin-fmt-merge-msg.c
index ebb3f37cf158dc479f364111893279805fa9a230,03c2bc33ebdc47c9f5d79f07aae833c3720a077d..7077d524776e748e0add4ff85478cd62512add15
@@@ -187,8 -187,7 +187,8 @@@ static void shortlog(const char *name, 
        add_pending_object(rev, branch, name);
        add_pending_object(rev, &head->object, "^HEAD");
        head->object.flags |= UNINTERESTING;
 -      prepare_revision_walk(rev);
 +      if (prepare_revision_walk(rev))
 +              die("revision walk setup failed");
        while ((commit = get_revision(rev)) != NULL) {
                char *oneline, *bol, *eol;
  
                        continue;
  
                bol = strstr(commit->buffer, "\n\n");
+               if (bol) {
+                       unsigned char c;
+                       do {
+                               c = *++bol;
+                       } while (isspace(c));
+                       if (!c)
+                               bol = NULL;
+               }
                if (!bol) {
                        append_to_list(&subjects, xstrdup(sha1_to_hex(
                                                        commit->object.sha1)),
                        continue;
                }
  
-               bol += 2;
                eol = strchr(bol, '\n');
                if (eol) {
                        oneline = xmemdupz(bol, eol - bol);
diff --combined git-am.sh
index ac5c388060789e559f80d61d6eae1db926b66ad4,0f05a2cfe301126a5390b3886f054f23c803e91c..245e1db1fc30c9446f42ae6b4626de854062afc7
+++ b/git-am.sh
@@@ -2,14 -2,13 +2,14 @@@
  #
  # Copyright (c) 2005, 2006 Junio C Hamano
  
 +SUBDIRECTORY_OK=Yes
  OPTIONS_KEEPDASHDASH=
  OPTIONS_SPEC="\
  git-am [options] <mbox>|<Maildir>...
  git-am [options] --resolved
  git-am [options] --skip
  --
 -d,dotest=       use <dir> and not .dotest
 +d,dotest=       (removed -- do not use)
  i,interactive   run interactively
  b,binary        pass --allo-binary-replacement to git-apply
  3,3way          allow fall back on 3way merging if needed
@@@ -21,14 -20,11 +21,14 @@@ C=              pass it through git-app
  p=              pass it through git-apply
  resolvemsg=     override error message when patch failure occurs
  r,resolved      to be used after a patch failure
 -skip            skip the current patch"
 +skip            skip the current patch
 +rebasing        (internal use for git-rebase)"
  
  . git-sh-setup
 +prefix=$(git rev-parse --show-prefix)
  set_reflog_action am
  require_work_tree
 +cd_to_toplevel
  
  git var GIT_COMMITTER_IDENT >/dev/null || exit
  
@@@ -51,6 -47,10 +51,6 @@@ stop_here_user_resolve () 
      then
          cmdline="$cmdline -3"
      fi
 -    if test '.dotest' != "$dotest"
 -    then
 -        cmdline="$cmdline -d=$dotest"
 -    fi
      echo "When you have resolved this problem run \"$cmdline --resolved\"."
      echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
  
@@@ -107,7 -107,7 +107,7 @@@ It does not apply to blobs recorded in 
      # patch did not touch, so recursive ends up canceling them,
      # saying that we reverted all those changes.
  
-     eval GITHEAD_$his_tree='"$SUBJECT"'
+     eval GITHEAD_$his_tree='"$FIRSTLINE"'
      export GITHEAD_$his_tree
      git-merge-recursive $orig_tree -- HEAD $his_tree || {
            git rerere
      unset GITHEAD_$his_tree
  }
  
- reread_subject () {
-       git stripspace <"$1" | sed -e 1q
- }
  prec=4
 -dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
 +dotest=".dotest"
 +sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
  resolvemsg= resume=
  git_apply_opt=
  
                resolved=t ;;
        --skip)
                skip=t ;;
 +      --rebasing)
 +              rebasing=t threeway=t keep=t binary=t ;;
        -d|--dotest)
 -              shift; dotest=$1;;
 +              die "-d option is no longer supported.  Do not use."
 +              ;;
        --resolvemsg)
                shift; resolvemsg=$1 ;;
        --whitespace)
@@@ -188,7 -180,7 +184,7 @@@ the
        0,)
                # No file input but without resume parameters; catch
                # user error to feed us a patch from standard input
 -              # when there is already .dotest.  This is somewhat
 +              # when there is already $dotest.  This is somewhat
                # unreliable -- stdin could be /dev/null for example
                # and the caller did not intend to feed us a patch but
                # wanted to continue unattended.
@@@ -208,24 -200,6 +204,24 @@@ els
        # Start afresh.
        mkdir -p "$dotest" || exit
  
 +      if test -n "$prefix" && test $# != 0
 +      then
 +              first=t
 +              for arg
 +              do
 +                      test -n "$first" && {
 +                              set x
 +                              first=
 +                      }
 +                      case "$arg" in
 +                      /*)
 +                              set "$@" "$arg" ;;
 +                      *)
 +                              set "$@" "$prefix$arg" ;;
 +                      esac
 +              done
 +              shift
 +      fi
        git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
                rm -fr "$dotest"
                exit 1
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
        echo 1 >"$dotest/next"
 +      if test -n "$rebasing"
 +      then
 +              : >"$dotest/rebasing"
 +      else
 +              : >"$dotest/applying"
 +      fi
  fi
  
  case "$resolved" in
                        echo "Patch is empty.  Was it split wrong?"
                        stop_here $this
                }
-               git stripspace < "$dotest/msg" > "$dotest/msg-clean"
+               SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
+               case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
+               (echo "$SUBJECT" ; echo ; cat "$dotest/msg") |
+                       git stripspace > "$dotest/msg-clean"
                ;;
        esac
  
  
        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
  
-       SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
-       case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
        case "$resume" in
        '')
            if test '' != "$SIGNOFF"
                LAST_SIGNED_OFF_BY=`
                    sed -ne '/^Signed-off-by: /p' \
                    "$dotest/msg-clean" |
 -                  tail -n 1
 +                  sed -ne '$p'
                `
                ADD_SIGNOFF=`
                    test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
                ADD_SIGNOFF=
            fi
            {
-               printf '%s\n' "$SUBJECT"
                if test -s "$dotest/msg-clean"
                then
-                       echo
                        cat "$dotest/msg-clean"
                fi
                if test '' != "$ADD_SIGNOFF"
                        ;;
                esac
        esac
+       FIRSTLINE=$(head -1 "$dotest/final-commit")
  
        resume=
        if test "$interactive" = t
                [aA]*) action=yes interactive= ;;
                [nN]*) action=skip ;;
                [eE]*) git_editor "$dotest/final-commit"
-                      SUBJECT=$(reread_subject "$dotest/final-commit")
+                      FIRSTLINE=$(head -1 "$dotest/final-commit")
                       action=again ;;
                [vV]*) action=again
                       LESS=-S ${PAGER:-less} "$dotest/patch" ;;
                stop_here $this
        fi
  
-       printf 'Applying %s\n' "$SUBJECT"
+       printf 'Applying %s\n' "$FIRSTLINE"
  
        case "$resolved" in
        '')
        tree=$(git write-tree) &&
        parent=$(git rev-parse --verify HEAD) &&
        commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
-       git update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
+       git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
        stop_here $this
  
        if test -x "$GIT_DIR"/hooks/post-applypatch
diff --combined git-bisect.sh
index 5177a2a11482af276ae5243c4d550b68bb0e2a28,b42f94cd39ef087313134361b5c8752f82c0e762..8e57e9a75dd9916d2c5a59f224859d1a22854c7f
@@@ -62,9 -62,10 +62,10 @@@ bisect_start() 
        # Verify HEAD. If we were bisecting before this, reset to the
        # top-of-line master first!
        #
-       head=$(GIT_DIR="$GIT_DIR" git symbolic-ref HEAD) ||
+       head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
        head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
        die "Bad HEAD - I need a HEAD"
+       start_head=''
        case "$head" in
        refs/heads/bisect)
                if [ -s "$GIT_DIR/BISECT_START" ]; then
@@@ -78,7 -79,7 +79,7 @@@
                # This error message should only be triggered by cogito usage,
                # and cogito users should understand it relates to cg-seek.
                [ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
-               echo "${head#refs/heads/}" >"$GIT_DIR/BISECT_START"
+               start_head="${head#refs/heads/}"
                ;;
        *)
                die "Bad HEAD - strange symbolic ref"
        done
        orig_args=$(sq "$@")
        bad_seen=0
+       eval=''
        while [ $# -gt 0 ]; do
            arg="$1"
            case "$arg" in
                0) state='bad' ; bad_seen=1 ;;
                *) state='good' ;;
                esac
-               bisect_write "$state" "$rev" 'nolog'
+               eval="$eval bisect_write '$state' '$rev' 'nolog'; "
                shift
                ;;
            esac
        done
  
        sq "$@" >"$GIT_DIR/BISECT_NAMES"
+       test -n "$start_head" && echo "$start_head" >"$GIT_DIR/BISECT_START"
+       eval "$eval"
        echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
        bisect_auto_next
  }
@@@ -153,12 -157,14 +157,14 @@@ bisect_state() 
                bisect_write "$state" "$rev" ;;
        2,bad|*,good|*,skip)
                shift
+               eval=''
                for rev in "$@"
                do
                        sha=$(git rev-parse --verify "$rev^{commit}") ||
                                die "Bad rev input: $rev"
-                       bisect_write "$state" "$sha"
-               done ;;
+                       eval="$eval bisect_write '$state' '$sha'; "
+               done
+               eval "$eval" ;;
        *,bad)
                die "'git bisect bad' can take only one argument." ;;
        *)
@@@ -289,14 -295,14 +295,14 @@@ bisect_next() 
        bisect_next_check good
  
        skip=$(git for-each-ref --format='%(objectname)' \
 -              "refs/bisect/skip-*" | tr '[\012]' ' ') || exit
 +              "refs/bisect/skip-*" | tr '\012' ' ') || exit
  
        BISECT_OPT=''
        test -n "$skip" && BISECT_OPT='--bisect-all'
  
        bad=$(git rev-parse --verify refs/bisect/bad) &&
        good=$(git for-each-ref --format='^%(objectname)' \
 -              "refs/bisect/good-*" | tr '[\012]' ' ') &&
 +              "refs/bisect/good-*" | tr '\012' ' ') &&
        eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
        eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
        eval=$(filter_skipped "$eval" "$skip") &&
@@@ -329,9 -335,9 +335,9 @@@ bisect_visualize() 
  
        if test $# = 0
        then
 -              case "${DISPLAY+set}" in
 +              case "${DISPLAY+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" in
                '')     set git log ;;
 -              set   set gitk ;;
 +              set*)   set gitk ;;
                esac
        else
                case "$1" in
index 32d6118183c20e90aca115f8feda9e3c72f94eaf,14e87967ff2e72ae9407099686dcf04bf6710444..5e3e5445c730140ad6c2fa9cc5bda4a7029e7fbf
@@@ -71,8 -71,12 +71,12 @@@ test_expect_success 'bisect start with 
        git bisect next
  '
  
- test_expect_success 'bisect good and bad fails if not given only revs' '
+ test_expect_success 'bisect fails if given any junk instead of revs' '
        git bisect reset &&
+       test_must_fail git bisect start foo $HASH1 -- &&
+       test_must_fail git bisect start $HASH4 $HASH1 bar -- &&
+       test -z "$(git for-each-ref "refs/bisect/*")" &&
+       test_must_fail ls .git/BISECT_* &&
        git bisect start &&
        test_must_fail git bisect good foo $HASH1 &&
        test_must_fail git bisect good $HASH1 bar &&
@@@ -80,6 -84,7 +84,7 @@@
        test_must_fail git bisect bad $HASH3 $HASH4 &&
        test_must_fail git bisect skip bar $HASH3 &&
        test_must_fail git bisect skip $HASH1 foo &&
+       test -z "$(git for-each-ref "refs/bisect/*")" &&
        git bisect good $HASH1 &&
        git bisect bad $HASH4
  '
@@@ -232,7 -237,7 +237,7 @@@ test_expect_success 'bisect run & skip
        add_line_into_file "6: Yet a line." hello &&
        HASH6=$(git rev-parse --verify HEAD) &&
        echo "#"\!"/bin/sh" > test_script.sh &&
 -      echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
 +      echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
        echo "grep line hello > /dev/null" >> test_script.sh &&
        echo "test \$? -ne 0" >> test_script.sh &&
        chmod +x test_script.sh &&
@@@ -257,8 -262,8 +262,8 @@@ test_expect_success 'bisect run & skip
        add_line_into_file "7: Should be the last line." hello &&
        HASH7=$(git rev-parse --verify HEAD) &&
        echo "#"\!"/bin/sh" > test_script.sh &&
 -      echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
 -      echo "tail -1 hello | grep day > /dev/null && exit 125" >> test_script.sh &&
 +      echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
 +      echo "sed -ne \\\$p hello | grep day > /dev/null && exit 125" >> test_script.sh &&
        echo "grep Yet hello > /dev/null" >> test_script.sh &&
        echo "test \$? -ne 0" >> test_script.sh &&
        chmod +x test_script.sh &&