Merge branch 'pk/rebase-in-c-3-acts'
authorJunio C Hamano <gitster@pobox.com>
Fri, 2 Nov 2018 02:04:54 +0000 (11:04 +0900)
committerJunio C Hamano <gitster@pobox.com>
Fri, 2 Nov 2018 02:04:54 +0000 (11:04 +0900)
Rewrite "git rebase" in C.

* pk/rebase-in-c-3-acts:
builtin rebase: stop if `git am` is in progress
builtin rebase: actions require a rebase in progress
builtin rebase: support --edit-todo and --show-current-patch
builtin rebase: support --quit
builtin rebase: support --abort
builtin rebase: support --skip
builtin rebase: support --continue

1  2 
builtin/rebase.c
strbuf.c
strbuf.h
diff --combined builtin/rebase.c
index 71367c8530e77035fe89ddfee8fcef0d77eaced0,2165656ae24a872a5faebc9fcac470aa933a194d..13084082776d6a72da0d1fe44cd2ad8163e07cb4
@@@ -21,7 -21,7 +21,8 @@@
  #include "diff.h"
  #include "wt-status.h"
  #include "revision.h"
 +#include "commit-reach.h"
+ #include "rerere.h"
  
  static char const * const builtin_rebase_usage[] = {
        N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@@ -92,6 -92,7 +93,7 @@@ struct rebase_options 
                REBASE_INTERACTIVE_EXPLICIT = 1<<4,
        } flags;
        struct strbuf git_am_opt;
+       const char *action;
  };
  
  static int is_interactive(struct rebase_options *opts)
@@@ -116,6 -117,62 +118,62 @@@ static const char *state_dir_path(cons
        return path.buf;
  }
  
+ /* Read one file, then strip line endings */
+ static int read_one(const char *path, struct strbuf *buf)
+ {
+       if (strbuf_read_file(buf, path, 0) < 0)
+               return error_errno(_("could not read '%s'"), path);
+       strbuf_trim_trailing_newline(buf);
+       return 0;
+ }
+ /* Initialize the rebase options from the state directory. */
+ static int read_basic_state(struct rebase_options *opts)
+ {
+       struct strbuf head_name = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct object_id oid;
+       if (read_one(state_dir_path("head-name", opts), &head_name) ||
+           read_one(state_dir_path("onto", opts), &buf))
+               return -1;
+       opts->head_name = starts_with(head_name.buf, "refs/") ?
+               xstrdup(head_name.buf) : NULL;
+       strbuf_release(&head_name);
+       if (get_oid(buf.buf, &oid))
+               return error(_("could not get 'onto': '%s'"), buf.buf);
+       opts->onto = lookup_commit_or_die(&oid, buf.buf);
+       /*
+        * We always write to orig-head, but interactive rebase used to write to
+        * head. Fall back to reading from head to cover for the case that the
+        * user upgraded git with an ongoing interactive rebase.
+        */
+       strbuf_reset(&buf);
+       if (file_exists(state_dir_path("orig-head", opts))) {
+               if (read_one(state_dir_path("orig-head", opts), &buf))
+                       return -1;
+       } else if (read_one(state_dir_path("head", opts), &buf))
+               return -1;
+       if (get_oid(buf.buf, &opts->orig_head))
+               return error(_("invalid orig-head: '%s'"), buf.buf);
+       strbuf_reset(&buf);
+       if (read_one(state_dir_path("quiet", opts), &buf))
+               return -1;
+       if (buf.len)
+               opts->flags &= ~REBASE_NO_QUIET;
+       else
+               opts->flags |= REBASE_NO_QUIET;
+       if (file_exists(state_dir_path("verbose", opts)))
+               opts->flags |= REBASE_VERBOSE;
+       strbuf_release(&buf);
+       return 0;
+ }
  static int finish_rebase(struct rebase_options *opts)
  {
        struct strbuf dir = STRBUF_INIT;
@@@ -169,12 -226,13 +227,13 @@@ static int run_specific_rebase(struct r
        add_var(&script_snippet, "state_dir", opts->state_dir);
  
        add_var(&script_snippet, "upstream_name", opts->upstream_name);
-       add_var(&script_snippet, "upstream",
-                                oid_to_hex(&opts->upstream->object.oid));
+       add_var(&script_snippet, "upstream", opts->upstream ?
+               oid_to_hex(&opts->upstream->object.oid) : NULL);
        add_var(&script_snippet, "head_name",
                opts->head_name ? opts->head_name : "detached HEAD");
        add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
-       add_var(&script_snippet, "onto", oid_to_hex(&opts->onto->object.oid));
+       add_var(&script_snippet, "onto", opts->onto ?
+               oid_to_hex(&opts->onto->object.oid) : NULL);
        add_var(&script_snippet, "onto_name", opts->onto_name);
        add_var(&script_snippet, "revisions", opts->revisions);
        add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
                opts->flags & REBASE_FORCE ? "t" : "");
        if (opts->switch_to)
                add_var(&script_snippet, "switch_to", opts->switch_to);
+       add_var(&script_snippet, "action", opts->action ? opts->action : "");
  
        switch (opts->type) {
        case REBASE_AM:
@@@ -401,12 -460,21 +461,21 @@@ int cmd_rebase(int argc, const char **a
                .git_am_opt = STRBUF_INIT,
        };
        const char *branch_name;
-       int ret, flags, in_progress = 0;
+       int ret, flags, total_argc, in_progress = 0;
        int ok_to_skip_pre_rebase = 0;
        struct strbuf msg = STRBUF_INIT;
        struct strbuf revisions = STRBUF_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct object_id merge_base;
+       enum {
+               NO_ACTION,
+               ACTION_CONTINUE,
+               ACTION_SKIP,
+               ACTION_ABORT,
+               ACTION_QUIT,
+               ACTION_EDIT_TODO,
+               ACTION_SHOW_CURRENT_PATCH,
+       } action = NO_ACTION;
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
                OPT_BIT(0, "no-ff", &options.flags,
                        N_("cherry-pick all commits, even if unchanged"),
                        REBASE_FORCE),
+               OPT_CMDMODE(0, "continue", &action, N_("continue"),
+                           ACTION_CONTINUE),
+               OPT_CMDMODE(0, "skip", &action,
+                           N_("skip current patch and continue"), ACTION_SKIP),
+               OPT_CMDMODE(0, "abort", &action,
+                           N_("abort and check out the original branch"),
+                           ACTION_ABORT),
+               OPT_CMDMODE(0, "quit", &action,
+                           N_("abort but keep HEAD where it is"), ACTION_QUIT),
+               OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
+                           "during an interactive rebase"), ACTION_EDIT_TODO),
+               OPT_CMDMODE(0, "show-current-patch", &action,
+                           N_("show the patch file being applied or merged"),
+                           ACTION_SHOW_CURRENT_PATCH),
                OPT_END(),
        };
  
  
        git_config(rebase_config, &options);
  
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "%s/applying", apply_dir());
+       if(file_exists(buf.buf))
+               die(_("It looks like 'git am' is in progress. Cannot rebase."));
        if (is_directory(apply_dir())) {
                options.type = REBASE_AM;
                options.state_dir = apply_dir();
        if (options.type != REBASE_UNSPECIFIED)
                in_progress = 1;
  
+       total_argc = argc;
        argc = parse_options(argc, argv, prefix,
                             builtin_rebase_options,
                             builtin_rebase_usage, 0);
  
+       if (action != NO_ACTION && total_argc != 2) {
+               usage_with_options(builtin_rebase_usage,
+                                  builtin_rebase_options);
+       }
        if (argc > 2)
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
  
+       if (action != NO_ACTION && !in_progress)
+               die(_("No rebase in progress?"));
+       if (action == ACTION_EDIT_TODO && !is_interactive(&options))
+               die(_("The --edit-todo action can only be used during "
+                     "interactive rebase."));
+       switch (action) {
+       case ACTION_CONTINUE: {
+               struct object_id head;
+               struct lock_file lock_file = LOCK_INIT;
+               int fd;
+               options.action = "continue";
+               /* Sanity check */
+               if (get_oid("HEAD", &head))
+                       die(_("Cannot read HEAD"));
+               fd = hold_locked_index(&lock_file, 0);
+               if (read_index(the_repository->index) < 0)
+                       die(_("could not read index"));
+               refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
+                             NULL);
+               if (0 <= fd)
+                       update_index_if_able(the_repository->index,
+                                            &lock_file);
+               rollback_lock_file(&lock_file);
+               if (has_unstaged_changes(1)) {
+                       puts(_("You must edit all merge conflicts and then\n"
+                              "mark them as resolved using git add"));
+                       exit(1);
+               }
+               if (read_basic_state(&options))
+                       exit(1);
+               goto run_rebase;
+       }
+       case ACTION_SKIP: {
+               struct string_list merge_rr = STRING_LIST_INIT_DUP;
+               options.action = "skip";
+               rerere_clear(&merge_rr);
+               string_list_clear(&merge_rr, 1);
+               if (reset_head(NULL, "reset", NULL, 0) < 0)
+                       die(_("could not discard worktree changes"));
+               if (read_basic_state(&options))
+                       exit(1);
+               goto run_rebase;
+       }
+       case ACTION_ABORT: {
+               struct string_list merge_rr = STRING_LIST_INIT_DUP;
+               options.action = "abort";
+               rerere_clear(&merge_rr);
+               string_list_clear(&merge_rr, 1);
+               if (read_basic_state(&options))
+                       exit(1);
+               if (reset_head(&options.orig_head, "reset",
+                              options.head_name, 0) < 0)
+                       die(_("could not move back to %s"),
+                           oid_to_hex(&options.orig_head));
+               ret = finish_rebase(&options);
+               goto cleanup;
+       }
+       case ACTION_QUIT: {
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, options.state_dir);
+               ret = !!remove_dir_recursively(&buf, 0);
+               if (ret)
+                       die(_("could not remove '%s'"), options.state_dir);
+               goto cleanup;
+       }
+       case ACTION_EDIT_TODO:
+               options.action = "edit-todo";
+               options.dont_finish_rebase = 1;
+               goto run_rebase;
+       case ACTION_SHOW_CURRENT_PATCH:
+               options.action = "show-current-patch";
+               options.dont_finish_rebase = 1;
+               goto run_rebase;
+       case NO_ACTION:
+               break;
+       default:
+               BUG("action: %d", action);
+       }
        /* Make sure no rebase is in progress */
        if (in_progress) {
                const char *last_slash = strrchr(options.state_dir, '/');
  
        options.revisions = revisions.buf;
  
+ run_rebase:
        ret = !!run_specific_rebase(&options);
  
  cleanup:
diff --combined strbuf.c
index 64041c3c249b158478ecc6db7257519bc3fc5c19,fdc0ffbafb35f78b139d27bf91b234ca4c65bc53..f6a6cf78b9426abfa73a2053c17326fd645645e7
+++ b/strbuf.c
@@@ -120,6 -120,15 +120,15 @@@ void strbuf_trim_trailing_dir_sep(struc
        sb->buf[sb->len] = '\0';
  }
  
+ void strbuf_trim_trailing_newline(struct strbuf *sb)
+ {
+       if (sb->len > 0 && sb->buf[sb->len - 1] == '\n') {
+               if (--sb->len > 0 && sb->buf[sb->len - 1] == '\r')
+                       --sb->len;
+               sb->buf[sb->len] = '\0';
+       }
+ }
  void strbuf_ltrim(struct strbuf *sb)
  {
        char *b = sb->buf;
  int strbuf_reencode(struct strbuf *sb, const char *from, const char *to)
  {
        char *out;
 -      int len;
 +      size_t len;
  
        if (same_encoding(from, to))
                return 0;
@@@ -209,7 -218,7 +218,7 @@@ void strbuf_list_free(struct strbuf **s
  
  int strbuf_cmp(const struct strbuf *a, const struct strbuf *b)
  {
 -      int len = a->len < b->len ? a->len: b->len;
 +      size_t len = a->len < b->len ? a->len: b->len;
        int cmp = memcmp(a->buf, b->buf, len);
        if (cmp)
                return cmp;
@@@ -389,7 -398,7 +398,7 @@@ size_t strbuf_expand_dict_cb(struct str
  
  void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
  {
 -      int i, len = src->len;
 +      size_t i, len = src->len;
  
        for (i = 0; i < len; i++) {
                if (src->buf[i] == '%')
@@@ -469,7 -478,7 +478,7 @@@ int strbuf_readlink(struct strbuf *sb, 
                hint = 32;
  
        while (hint < STRBUF_MAXLINK) {
 -              int len;
 +              ssize_t len;
  
                strbuf_grow(sb, hint);
                len = readlink(path, sb->buf, hint);
@@@ -734,18 -743,18 +743,18 @@@ void strbuf_humanise_bytes(struct strbu
  {
        if (bytes > 1 << 30) {
                strbuf_addf(buf, "%u.%2.2u GiB",
 -                          (int)(bytes >> 30),
 -                          (int)(bytes & ((1 << 30) - 1)) / 10737419);
 +                          (unsigned)(bytes >> 30),
 +                          (unsigned)(bytes & ((1 << 30) - 1)) / 10737419);
        } else if (bytes > 1 << 20) {
 -              int x = bytes + 5243;  /* for rounding */
 +              unsigned x = bytes + 5243;  /* for rounding */
                strbuf_addf(buf, "%u.%2.2u MiB",
                            x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
        } else if (bytes > 1 << 10) {
 -              int x = bytes + 5;  /* for rounding */
 +              unsigned x = bytes + 5;  /* for rounding */
                strbuf_addf(buf, "%u.%2.2u KiB",
                            x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
        } else {
 -              strbuf_addf(buf, "%u bytes", (int)bytes);
 +              strbuf_addf(buf, "%u bytes", (unsigned)bytes);
        }
  }
  
@@@ -960,7 -969,7 +969,7 @@@ static size_t cleanup(char *line, size_
   */
  void strbuf_stripspace(struct strbuf *sb, int skip_comments)
  {
 -      int empties = 0;
 +      size_t empties = 0;
        size_t i, j, len, newlen;
        char *eol;
  
diff --combined strbuf.h
index 9981f782b2573c1a4360879e03884ec753999aec,b7aea8a9660160af9edf244fc0626009374b98c3..fc40873b65124fc98a2e06150b7c8043f05ed4ab
+++ b/strbuf.h
@@@ -87,7 -87,7 +87,7 @@@ struct object_id
   * Initialize the structure. The second parameter can be zero or a bigger
   * number to allocate memory, in case you want to prevent further reallocs.
   */
 -extern void strbuf_init(struct strbuf *, size_t);
 +void strbuf_init(struct strbuf *sb, size_t alloc);
  
  /**
   * Release a string buffer and the memory it used. After this call, the
@@@ -97,7 -97,7 +97,7 @@@
   * To clear a strbuf in preparation for further use without the overhead
   * of free()ing and malloc()ing again, use strbuf_reset() instead.
   */
 -extern void strbuf_release(struct strbuf *);
 +void strbuf_release(struct strbuf *sb);
  
  /**
   * Detach the string from the strbuf and returns it; you now own the
   * The strbuf that previously held the string is reset to `STRBUF_INIT` so
   * it can be reused after calling this function.
   */
 -extern char *strbuf_detach(struct strbuf *, size_t *);
 +char *strbuf_detach(struct strbuf *sb, size_t *sz);
  
  /**
   * Attach a string to a buffer. You should specify the string to attach,
   * malloc()ed, and after attaching, the pointer cannot be relied upon
   * anymore, and neither be free()d directly.
   */
 -extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
 +void strbuf_attach(struct strbuf *sb, void *str, size_t len, size_t mem);
  
  /**
   * Swap the contents of two string buffers.
@@@ -148,7 -148,7 +148,7 @@@ static inline size_t strbuf_avail(cons
   * This is never a needed operation, but can be critical for performance in
   * some cases.
   */
 -extern void strbuf_grow(struct strbuf *, size_t);
 +void strbuf_grow(struct strbuf *sb, size_t amount);
  
  /**
   * Set the length of the buffer to a given value. This function does *not*
@@@ -183,30 -183,33 +183,33 @@@ static inline void strbuf_setlen(struc
   * Strip whitespace from the beginning (`ltrim`), end (`rtrim`), or both side
   * (`trim`) of a string.
   */
 -extern void strbuf_trim(struct strbuf *);
 -extern void strbuf_rtrim(struct strbuf *);
 -extern void strbuf_ltrim(struct strbuf *);
 +void strbuf_trim(struct strbuf *sb);
 +void strbuf_rtrim(struct strbuf *sb);
 +void strbuf_ltrim(struct strbuf *sb);
  
  /* Strip trailing directory separators */
 -extern void strbuf_trim_trailing_dir_sep(struct strbuf *);
 +void strbuf_trim_trailing_dir_sep(struct strbuf *sb);
  
 -extern void strbuf_trim_trailing_newline(struct strbuf *sb);
+ /* Strip trailing LF or CR/LF */
++void strbuf_trim_trailing_newline(struct strbuf *sb);
  /**
   * Replace the contents of the strbuf with a reencoded form.  Returns -1
   * on error, 0 on success.
   */
 -extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 +int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
  
  /**
   * Lowercase each character in the buffer using `tolower`.
   */
 -extern void strbuf_tolower(struct strbuf *sb);
 +void strbuf_tolower(struct strbuf *sb);
  
  /**
   * Compare two buffers. Returns an integer less than, equal to, or greater
   * than zero if the first buffer is found, respectively, to be less than,
   * to match, or be greater than the second buffer.
   */
 -extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
 +int strbuf_cmp(const struct strbuf *first, const struct strbuf *second);
  
  
  /**
@@@ -233,38 -236,37 +236,38 @@@ static inline void strbuf_addch(struct 
  /**
   * Add a character the specified number of times to the buffer.
   */
 -extern void strbuf_addchars(struct strbuf *sb, int c, size_t n);
 +void strbuf_addchars(struct strbuf *sb, int c, size_t n);
  
  /**
   * Insert data to the given position of the buffer. The remaining contents
   * will be shifted, not overwritten.
   */
 -extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t);
 +void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
  
  /**
   * Remove given amount of data from a given position of the buffer.
   */
 -extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
 +void strbuf_remove(struct strbuf *sb, size_t pos, size_t len);
  
  /**
   * Remove the bytes between `pos..pos+len` and replace it with the given
   * data.
   */
 -extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
 -                        const void *, size_t);
 +void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
 +                 const void *data, size_t data_len);
  
  /**
   * Add a NUL-terminated string to the buffer. Each line will be prepended
   * by a comment character and a blank.
   */
 -extern void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size);
 +void strbuf_add_commented_lines(struct strbuf *out,
 +                              const char *buf, size_t size);
  
  
  /**
   * Add data of given length to the buffer.
   */
 -extern void strbuf_add(struct strbuf *, const void *, size_t);
 +void strbuf_add(struct strbuf *sb, const void *data, size_t len);
  
  /**
   * Add a NUL-terminated string to the buffer.
@@@ -283,7 -285,7 +286,7 @@@ static inline void strbuf_addstr(struc
  /**
   * Copy the contents of another buffer at the end of the current one.
   */
 -extern void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
 +void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
  
  /**
   * This function can be used to expand a format string containing
   * parameters to the callback, `strbuf_expand()` passes a context pointer,
   * which can be used by the programmer of the callback as she sees fit.
   */
 -typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
 -extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
 +typedef size_t (*expand_fn_t) (struct strbuf *sb,
 +                             const char *placeholder,
 +                             void *context);
 +void strbuf_expand(struct strbuf *sb,
 +                 const char *format,
 +                 expand_fn_t fn,
 +                 void *context);
  
  /**
   * Used as callback for `strbuf_expand()`, expects an array of
@@@ -327,9 -324,7 +330,9 @@@ struct strbuf_expand_dict_entry 
        const char *placeholder;
        const char *value;
  };
 -extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
 +size_t strbuf_expand_dict_cb(struct strbuf *sb,
 +                           const char *placeholder,
 +                           void *context);
  
  /**
   * Append the contents of one strbuf to another, quoting any
   * destination. This is useful for literal data to be fed to either
   * strbuf_expand or to the *printf family of functions.
   */
 -extern void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
 +void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
  
  /**
   * Append the given byte size as a human-readable string (i.e. 12.23 KiB,
   * 3.50 MiB).
   */
 -extern void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
 +void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
  
  /**
   * Add a formatted string to the buffer.
   */
  __attribute__((format (printf,2,3)))
 -extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 +void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
  
  /**
   * Add a formatted string prepended by a comment character and a
   * blank to the buffer.
   */
  __attribute__((format (printf, 2, 3)))
 -extern void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
 +void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...);
  
  __attribute__((format (printf,2,0)))
 -extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
 +void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
  
  /**
   * Add the time specified by `tm`, as formatted by `strftime`.
   * `suppress_tz_name`, when set, expands %Z internally to the empty
   * string rather than passing it to `strftime`.
   */
 -extern void strbuf_addftime(struct strbuf *sb, const char *fmt,
 -                          const struct tm *tm, int tz_offset,
 -                          int suppress_tz_name);
 +void strbuf_addftime(struct strbuf *sb, const char *fmt,
 +                  const struct tm *tm, int tz_offset,
 +                  int suppress_tz_name);
  
  /**
   * Read a given size of data from a FILE* pointer to the buffer.
   * `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline_*()`
   * family of functions have the same behaviour as well.
   */
 -extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
 +size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *file);
  
  /**
   * Read the contents of a given file descriptor. The third argument can be
   * used to give a hint about the file size, to avoid reallocs.  If read fails,
   * any partial read is undone.
   */
 -extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
 +ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint);
  
  /**
   * Read the contents of a given file descriptor partially by using only one
   * file size, to avoid reallocs. Returns the number of new bytes appended to
   * the sb.
   */
 -extern ssize_t strbuf_read_once(struct strbuf *, int fd, size_t hint);
 +ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint);
  
  /**
   * Read the contents of a file, specified by its path. The third argument
   * Return the number of bytes read or a negative value if some error
   * occurred while opening or reading the file.
   */
 -extern ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 +ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
  
  /**
   * Read the target of a symbolic link, specified by its path.  The third
   * argument can be used to give a hint about the size, to avoid reallocs.
   */
 -extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 +int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
  
  /**
   * Write the whole content of the strbuf to the stream not stopping at
   * NUL bytes.
   */
 -extern ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
 +ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
  
  /**
   * Read a line from a FILE *, overwriting the existing contents of
  typedef int (*strbuf_getline_fn)(struct strbuf *, FILE *);
  
  /* Uses LF as the line terminator */
 -extern int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
 +int strbuf_getline_lf(struct strbuf *sb, FILE *fp);
  
  /* Uses NUL as the line terminator */
 -extern int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
 +int strbuf_getline_nul(struct strbuf *sb, FILE *fp);
  
  /*
   * Similar to strbuf_getline_lf(), but additionally treats a CR that
   * that can come from platforms whose native text format is CRLF
   * terminated.
   */
 -extern int strbuf_getline(struct strbuf *, FILE *);
 +int strbuf_getline(struct strbuf *sb, FILE *file);
  
  
  /**
   * Like `strbuf_getline`, but keeps the trailing terminator (if
   * any) in the buffer.
   */
 -extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
 +int strbuf_getwholeline(struct strbuf *sb, FILE *file, int term);
  
  /**
   * Like `strbuf_getwholeline`, but operates on a file descriptor.
   * use it unless you need the correct position in the file
   * descriptor.
   */
 -extern int strbuf_getwholeline_fd(struct strbuf *, int, int);
 +int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term);
  
  /**
   * Set the buffer to the path of the current working directory.
   */
 -extern int strbuf_getcwd(struct strbuf *sb);
 +int strbuf_getcwd(struct strbuf *sb);
  
  /**
   * Add a path to a buffer, converting a relative path to an
   * absolute one in the process.  Symbolic links are not
   * resolved.
   */
 -extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
 +void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
  
  /**
   * Canonize `path` (make it absolute, resolve symlinks, remove extra
   * Callers that don't mind links should use the more lightweight
   * strbuf_add_absolute_path() instead.
   */
 -extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
 +void strbuf_add_real_path(struct strbuf *sb, const char *path);
  
  
  /**
   * normalize_path_copy() for details. If an error occurs, the contents of "sb"
   * are left untouched, and -1 is returned.
   */
 -extern int strbuf_normalize_path(struct strbuf *sb);
 +int strbuf_normalize_path(struct strbuf *sb);
  
  /**
   * Strip whitespace from a buffer. The second parameter controls if
   * comments are considered contents to be removed or not.
   */
 -extern void strbuf_stripspace(struct strbuf *buf, int skip_comments);
 +void strbuf_stripspace(struct strbuf *buf, int skip_comments);
  
  static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
  {
   * For lighter-weight alternatives, see string_list_split() and
   * string_list_split_in_place().
   */
 -extern struct strbuf **strbuf_split_buf(const char *, size_t,
 -                                      int terminator, int max);
 +struct strbuf **strbuf_split_buf(const char *str, size_t len,
 +                               int terminator, int max);
  
  static inline struct strbuf **strbuf_split_str(const char *str,
                                               int terminator, int max)
  }
  
  static inline struct strbuf **strbuf_split_max(const struct strbuf *sb,
 -                                              int terminator, int max)
 +                                             int terminator, int max)
  {
        return strbuf_split_buf(sb->buf, sb->len, terminator, max);
  }
@@@ -557,23 -552,23 +560,23 @@@ static inline struct strbuf **strbuf_sp
   *   'element1, element2, ..., elementN'
   * to str.  If only one element, just write "element1" to str.
   */
 -extern void strbuf_add_separated_string_list(struct strbuf *str,
 -                                           const char *sep,
 -                                           struct string_list *slist);
 +void strbuf_add_separated_string_list(struct strbuf *str,
 +                                    const char *sep,
 +                                    struct string_list *slist);
  
  /**
   * Free a NULL-terminated list of strbufs (for example, the return
   * values of the strbuf_split*() functions).
   */
 -extern void strbuf_list_free(struct strbuf **);
 +void strbuf_list_free(struct strbuf **list);
  
  /**
   * Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
   * the strbuf `sb`.
   */
 -extern void strbuf_add_unique_abbrev(struct strbuf *sb,
 -                                   const struct object_id *oid,
 -                                   int abbrev_len);
 +void strbuf_add_unique_abbrev(struct strbuf *sb,
 +                            const struct object_id *oid,
 +                            int abbrev_len);
  
  /**
   * Launch the user preferred editor to edit a file and fill the buffer
   * run in. If the buffer is NULL the editor is launched as usual but the
   * file's contents are not read into the buffer upon completion.
   */
 -extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
 +int launch_editor(const char *path, struct strbuf *buffer,
 +                const char *const *env);
 +
 +int launch_sequence_editor(const char *path, struct strbuf *buffer,
 +                         const char *const *env);
  
 -extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
 +void strbuf_add_lines(struct strbuf *sb,
 +                    const char *prefix,
 +                    const char *buf,
 +                    size_t size);
  
  /**
   * Append s to sb, with the characters '<', '>', '&' and '"' converted
   * into XML entities.
   */
 -extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s);
 +void strbuf_addstr_xml_quoted(struct strbuf *sb,
 +                            const char *s);
  
  /**
   * "Complete" the contents of `sb` by ensuring that either it ends with the
@@@ -628,8 -615,8 +631,8 @@@ static inline void strbuf_complete_line
   * If "allowed" is non-zero, restrict the set of allowed expansions. See
   * interpret_branch_name() for details.
   */
 -extern void strbuf_branchname(struct strbuf *sb, const char *name,
 -                            unsigned allowed);
 +void strbuf_branchname(struct strbuf *sb, const char *name,
 +                     unsigned allowed);
  
  /*
   * Like strbuf_branchname() above, but confirm that the result is
   *
   * The return value is "0" if the result is valid, and "-1" otherwise.
   */
 -extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 +int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
  
 -extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
 -                                  int reserved);
 +void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
 +                           int reserved);
  
  __attribute__((format (printf,1,2)))
 -extern int printf_ln(const char *fmt, ...);
 +int printf_ln(const char *fmt, ...);
  __attribute__((format (printf,2,3)))
 -extern int fprintf_ln(FILE *fp, const char *fmt, ...);
 +int fprintf_ln(FILE *fp, const char *fmt, ...);
  
  char *xstrdup_tolower(const char *);
  char *xstrdup_toupper(const char *);