Merge branch 'lb/rebase-i-short-command-names'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Dec 2017 19:16:21 +0000 (11:16 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Dec 2017 19:16:21 +0000 (11:16 -0800)
With a configuration variable rebase.abbreviateCommands set,
"git rebase -i" produces the todo list with a single-letter
command names.

* lb/rebase-i-short-command-names:
sequencer.c: drop 'const' from function return type
t3404: add test case for abbreviated commands
rebase -i: learn to abbreviate command names
rebase -i -x: add exec commands via the rebase--helper
rebase -i: update functions to use a flags parameter
rebase -i: replace reference to sha1 with oid
rebase -i: refactor transform_todo_ids
rebase -i: set commit to null in exec commands
Documentation: use preferred name for the 'todo list' script
Documentation: move rebase.* configs to new file

1  2 
Documentation/config.txt
sequencer.c
diff --combined Documentation/config.txt
index b18c0f97fec8dda4ba90a245f8e827b57a585cf4,e424b7de90b5f8af54125d9853b8044e17cdb800..7814fb904415bcb3b520367702c6f5145cac6336
@@@ -354,9 -354,6 +354,9 @@@ advice.*:
        ignoredHook::
                Advice shown if an hook is ignored because the hook is not
                set as executable.
 +      waitingForEditor::
 +              Print a message to the terminal whenever Git is waiting for
 +              editor input from the user.
  --
  
  core.fileMode::
@@@ -2111,40 -2108,15 +2111,40 @@@ matched against are those given directl
  visited as a result of a redirection do not participate in matching.
  
  ssh.variant::
 -      Depending on the value of the environment variables `GIT_SSH` or
 -      `GIT_SSH_COMMAND`, or the config setting `core.sshCommand`, Git
 -      auto-detects whether to adjust its command-line parameters for use
 -      with plink or tortoiseplink, as opposed to the default (OpenSSH).
 +      By default, Git determines the command line arguments to use
 +      based on the basename of the configured SSH command (configured
 +      using the environment variable `GIT_SSH` or `GIT_SSH_COMMAND` or
 +      the config setting `core.sshCommand`). If the basename is
 +      unrecognized, Git will attempt to detect support of OpenSSH
 +      options by first invoking the configured SSH command with the
 +      `-G` (print configuration) option and will subsequently use
 +      OpenSSH options (if that is successful) or no options besides
 +      the host and remote command (if it fails).
 ++
 +The config variable `ssh.variant` can be set to override this detection.
 +Valid values are `ssh` (to use OpenSSH options), `plink`, `putty`,
 +`tortoiseplink`, `simple` (no options except the host and remote command).
 +The default auto-detection can be explicitly requested using the value
 +`auto`.  Any other value is treated as `ssh`.  This setting can also be
 +overridden via the environment variable `GIT_SSH_VARIANT`.
 ++
 +The current command-line parameters used for each variant are as
 +follows:
  +
 -The config variable `ssh.variant` can be set to override this auto-detection;
 -valid values are `ssh`, `plink`, `putty` or `tortoiseplink`. Any other value
 -will be treated as normal ssh. This setting can be overridden via the
 -environment variable `GIT_SSH_VARIANT`.
 +--
 +
 +* `ssh` - [-p port] [-4] [-6] [-o option] [username@]host command
 +
 +* `simple` - [username@]host command
 +
 +* `plink` or `putty` - [-P port] [-4] [-6] [username@]host command
 +
 +* `tortoiseplink` - [-P port] [-4] [-6] -batch [username@]host command
 +
 +--
 ++
 +Except for the `simple` variant, command-line parameters are likely to
 +change as git gains new features.
  
  i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
@@@ -2572,23 -2544,6 +2572,23 @@@ The protocol names currently used by gi
      `hg` to allow the `git-remote-hg` helper)
  --
  
 +protocol.version::
 +      Experimental. If set, clients will attempt to communicate with a
 +      server using the specified protocol version.  If unset, no
 +      attempt will be made by the client to communicate using a
 +      particular protocol version, this results in protocol version 0
 +      being used.
 +      Supported versions:
 ++
 +--
 +
 +* `0` - the original wire protocol.
 +
 +* `1` - the original wire protocol with the addition of a version string
 +  in the initial response from the server.
 +
 +--
 +
  pull.ff::
        By default, Git does not create an extra merge commit when merging
        a commit that is a descendant of the current commit. Instead, the
@@@ -2736,36 -2691,7 +2736,7 @@@ push.recurseSubmodules:
        is retained. You may override this configuration at time of push by
        specifying '--recurse-submodules=check|on-demand|no'.
  
- rebase.stat::
-       Whether to show a diffstat of what changed upstream since the last
-       rebase. False by default.
- rebase.autoSquash::
-       If set to true enable `--autosquash` option by default.
- rebase.autoStash::
-       When set to true, automatically create a temporary stash entry
-       before the operation begins, and apply it after the operation
-       ends.  This means that you can run rebase on a dirty worktree.
-       However, use with care: the final stash application after a
-       successful rebase might result in non-trivial conflicts.
-       Defaults to false.
- rebase.missingCommitsCheck::
-       If set to "warn", git rebase -i will print a warning if some
-       commits are removed (e.g. a line was deleted), however the
-       rebase will still proceed. If set to "error", it will print
-       the previous warning and stop the rebase, 'git rebase
-       --edit-todo' can then be used to correct the error. If set to
-       "ignore", no checking is done.
-       To drop a commit without warning or error, use the `drop`
-       command in the todo-list.
-       Defaults to "ignore".
- rebase.instructionFormat::
-       A format string, as specified in linkgit:git-log[1], to be used for
-       the instruction list during an interactive rebase.  The format will automatically
-       have the long commit hash prepended to the format.
+ include::rebase-config.txt[]
  
  receive.advertiseAtomic::
        By default, git-receive-pack will advertise the atomic push
@@@ -3471,13 -3397,3 +3442,13 @@@ web.browser:
        Specify a web browser that may be used by some commands.
        Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
        may use it.
 +
 +worktree.guessRemote::
 +      With `add`, if no branch argument, and neither of `-b` nor
 +      `-B` nor `--detach` are given, the command defaults to
 +      creating a new branch from HEAD.  If `worktree.guessRemote` is
 +      set to true, `worktree add` tries to find a remote-tracking
 +      branch whose name uniquely matches the new branch name.  If
 +      such a branch exists, it is checked out and set as "upstream"
 +      for the new branch.  If no such match can be found, it falls
 +      back to creating a new branch from the current HEAD.
diff --combined sequencer.c
index c8cb20c1ebdd03f7897eb0ccecbb49d9ef9fd390,2a407cbe54870204476c1534bc048d09900df310..894d12ad725c2f8cdbabbc87c7db558f5a872a4e
@@@ -347,7 -347,7 +347,7 @@@ static int read_oneliner(struct strbuf 
  
  static struct tree *empty_tree(void)
  {
 -      return lookup_tree(&empty_tree_oid);
 +      return lookup_tree(the_hash_algo->empty_tree);
  }
  
  static int error_dirty_index(struct replay_opts *opts)
@@@ -449,7 -449,6 +449,7 @@@ static int do_recursive_merge(struct co
        o.branch2 = next ? next_label : "(empty tree)";
        if (is_rebase_i(opts))
                o.buffer_output = 2;
 +      o.show_rename_progress = 1;
  
        head_tree = parse_tree_indirect(head);
        next_tree = next ? next->tree : empty_tree();
        if (is_rebase_i(opts) && clean <= 0)
                fputs(o.obuf.buf, stdout);
        strbuf_release(&o.obuf);
 +      diff_warn_rename_limit("merge.renamelimit", o.needed_rename_limit, 0);
        if (clean < 0)
                return clean;
  
@@@ -708,7 -706,7 +708,7 @@@ static int is_original_commit_empty(str
                                oid_to_hex(&parent->object.oid));
                ptree_oid = &parent->tree->object.oid;
        } else {
 -              ptree_oid = &empty_tree_oid; /* commit is root */
 +              ptree_oid = the_hash_algo->empty_tree; /* commit is root */
        }
  
        return !oidcmp(ptree_oid, &commit->tree->object.oid);
@@@ -797,6 -795,13 +797,13 @@@ static const char *command_to_string(co
        die("Unknown command: %d", command);
  }
  
+ static char command_to_char(const enum todo_command command)
+ {
+       if (command < TODO_COMMENT && todo_command_info[command].c)
+               return todo_command_info[command].c;
+       return comment_line_char;
+ }
  static int is_noop(const enum todo_command command)
  {
        return TODO_NOOP <= command;
@@@ -961,7 -966,7 +968,7 @@@ static int do_pick_commit(enum todo_com
        } else {
                unborn = get_oid("HEAD", &head);
                if (unborn)
 -                      oidcpy(&head, &empty_tree_oid);
 +                      oidcpy(&head, the_hash_algo->empty_tree);
                if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD",
                                       NULL, 0))
                        return error_dirty_index(opts);
@@@ -1270,6 -1275,7 +1277,7 @@@ static int parse_insn_line(struct todo_
        bol += padding;
  
        if (item->command == TODO_EXEC) {
+               item->commit = NULL;
                item->arg = bol;
                item->arg_len = (int)(eol - bol);
                return 0;
@@@ -2445,14 -2451,16 +2453,16 @@@ void append_signoff(struct strbuf *msgb
        strbuf_release(&sob);
  }
  
- int sequencer_make_script(int keep_empty, FILE *out,
-               int argc, const char **argv)
+ int sequencer_make_script(FILE *out, int argc, const char **argv,
+                         unsigned flags)
  {
        char *format = NULL;
        struct pretty_print_context pp = {0};
        struct strbuf buf = STRBUF_INIT;
        struct rev_info revs;
        struct commit *commit;
+       int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
+       const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
  
        init_revisions(&revs, NULL);
        revs.verbose_header = 1;
                strbuf_reset(&buf);
                if (!keep_empty && is_original_commit_empty(commit))
                        strbuf_addf(&buf, "%c ", comment_line_char);
-               strbuf_addf(&buf, "pick %s ", oid_to_hex(&commit->object.oid));
+               strbuf_addf(&buf, "%s %s ", insn,
+                           oid_to_hex(&commit->object.oid));
                pretty_print_commit(&pp, commit, &buf);
                strbuf_addch(&buf, '\n');
                fputs(buf.buf, out);
        return 0;
  }
  
- int transform_todo_ids(int shorten_ids)
+ /*
+  * Add commands after pick and (series of) squash/fixup commands
+  * in the todo list.
+  */
+ int sequencer_add_exec_commands(const char *commands)
  {
        const char *todo_file = rebase_path_todo();
        struct todo_list todo_list = TODO_LIST_INIT;
-       int fd, res, i;
-       FILE *out;
+       struct todo_item *item;
+       struct strbuf *buf = &todo_list.buf;
+       size_t offset = 0, commands_len = strlen(commands);
+       int i, first;
  
-       strbuf_reset(&todo_list.buf);
-       fd = open(todo_file, O_RDONLY);
-       if (fd < 0)
-               return error_errno(_("could not open '%s'"), todo_file);
-       if (strbuf_read(&todo_list.buf, fd, 0) < 0) {
-               close(fd);
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
                return error(_("could not read '%s'."), todo_file);
-       }
-       close(fd);
  
-       res = parse_insn_buffer(todo_list.buf.buf, &todo_list);
-       if (res) {
+       if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
                todo_list_release(&todo_list);
                return error(_("unusable todo list: '%s'"), todo_file);
        }
  
-       out = fopen(todo_file, "w");
-       if (!out) {
+       first = 1;
+       /* insert <commands> before every pick except the first one */
+       for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+               if (item->command == TODO_PICK && !first) {
+                       strbuf_insert(buf, item->offset_in_buf + offset,
+                                     commands, commands_len);
+                       offset += commands_len;
+               }
+               first = 0;
+       }
+       /* append final <commands> */
+       strbuf_add(buf, commands, commands_len);
+       i = write_message(buf->buf, buf->len, todo_file, 0);
+       todo_list_release(&todo_list);
+       return i;
+ }
+ int transform_todos(unsigned flags)
+ {
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct todo_item *item;
+       int i;
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error(_("could not read '%s'."), todo_file);
+       if (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {
                todo_list_release(&todo_list);
-               return error(_("unable to open '%s' for writing"), todo_file);
-       }
-       for (i = 0; i < todo_list.nr; i++) {
-               struct todo_item *item = todo_list.items + i;
-               int bol = item->offset_in_buf;
-               const char *p = todo_list.buf.buf + bol;
-               int eol = i + 1 < todo_list.nr ?
-                       todo_list.items[i + 1].offset_in_buf :
-                       todo_list.buf.len;
-               if (item->command >= TODO_EXEC && item->command != TODO_DROP)
-                       fwrite(p, eol - bol, 1, out);
-               else {
-                       const char *id = shorten_ids ?
-                               short_commit_name(item->commit) :
-                               oid_to_hex(&item->commit->object.oid);
-                       int len;
-                       p += strspn(p, " \t"); /* left-trim command */
-                       len = strcspn(p, " \t"); /* length of command */
-                       fprintf(out, "%.*s %s %.*s\n",
-                               len, p, id, item->arg_len, item->arg);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+       for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+               /* if the item is not a command write it and continue */
+               if (item->command >= TODO_COMMENT) {
+                       strbuf_addf(&buf, "%.*s\n", item->arg_len, item->arg);
+                       continue;
                }
+               /* add command to the buffer */
+               if (flags & TODO_LIST_ABBREVIATE_CMDS)
+                       strbuf_addch(&buf, command_to_char(item->command));
+               else
+                       strbuf_addstr(&buf, command_to_string(item->command));
+               /* add commit id */
+               if (item->commit) {
+                       const char *oid = flags & TODO_LIST_SHORTEN_IDS ?
+                                         short_commit_name(item->commit) :
+                                         oid_to_hex(&item->commit->object.oid);
+                       strbuf_addf(&buf, " %s", oid);
+               }
+               /* add all the rest */
+               strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
        }
-       fclose(out);
+       i = write_message(buf.buf, buf.len, todo_file, 0);
        todo_list_release(&todo_list);
-       return 0;
+       return i;
  }
  
  enum check_level {