[PATCH] p3400: replace calls to `git checkout -b' by `git checkout -B'

2018-11-09 Thread Alban Gruin
p3400 makes a copy of the current repository to test git-rebase
performance, and creates new branches in the copy with `git checkout
-b'.  If the original repository has branches with the same name as the
script is trying to create, this operation will fail.

This replaces these calls by `git checkout -B' to force the creation and
update of these branches.

Signed-off-by: Alban Gruin 
---
 t/perf/p3400-rebase.sh | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/t/perf/p3400-rebase.sh b/t/perf/p3400-rebase.sh
index ce271ca4c1..d202aaed06 100755
--- a/t/perf/p3400-rebase.sh
+++ b/t/perf/p3400-rebase.sh
@@ -6,9 +6,9 @@ test_description='Tests rebase performance'
 test_perf_default_repo
 
 test_expect_success 'setup rebasing on top of a lot of changes' '
-   git checkout -f -b base &&
-   git checkout -b to-rebase &&
-   git checkout -b upstream &&
+   git checkout -f -B base &&
+   git checkout -B to-rebase &&
+   git checkout -B upstream &&
for i in $(seq 100)
do
# simulate huge diffs
@@ -35,8 +35,8 @@ test_perf 'rebase on top of a lot of unrelated changes' '
 
 test_expect_success 'setup rebasing many changes without split-index' '
git config core.splitIndex false &&
-   git checkout -b upstream2 to-rebase &&
-   git checkout -b to-rebase2 upstream
+   git checkout -B upstream2 to-rebase &&
+   git checkout -B to-rebase2 upstream
 '
 
 test_perf 'rebase a lot of unrelated changes without split-index' '
-- 
2.19.1



[PATCH v3 02/16] sequencer: make the todo_list structure public

2018-11-09 Thread Alban Gruin
This makes the structures todo_list and todo_item, and the functions
todo_list_release() and parse_insn_buffer(), accessible outside of
sequencer.c.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 67 +
 sequencer.h | 49 +++
 2 files changed, 60 insertions(+), 56 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 07cc91d8db..7adbeaa27d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1443,32 +1443,6 @@ static int allow_empty(struct replay_opts *opts, struct 
commit *commit)
return 1;
 }
 
-/*
- * Note that ordering matters in this enum. Not only must it match the mapping
- * below, it is also divided into several sections that matter.  When adding
- * new commands, make sure you add it in the right section.
- */
-enum todo_command {
-   /* commands that handle commits */
-   TODO_PICK = 0,
-   TODO_REVERT,
-   TODO_EDIT,
-   TODO_REWORD,
-   TODO_FIXUP,
-   TODO_SQUASH,
-   /* commands that do something else than handling a single commit */
-   TODO_EXEC,
-   TODO_BREAK,
-   TODO_LABEL,
-   TODO_RESET,
-   TODO_MERGE,
-   /* commands that do nothing but are counted for reporting progress */
-   TODO_NOOP,
-   TODO_DROP,
-   /* comments (not counted for reporting progress) */
-   TODO_COMMENT
-};
-
 static struct {
char c;
const char *str;
@@ -1939,26 +1913,7 @@ enum todo_item_flags {
TODO_EDIT_MERGE_MSG = 1
 };
 
-struct todo_item {
-   enum todo_command command;
-   struct commit *commit;
-   unsigned int flags;
-   const char *arg;
-   int arg_len;
-   size_t offset_in_buf;
-};
-
-struct todo_list {
-   struct strbuf buf;
-   struct todo_item *items;
-   int nr, alloc, current;
-   int done_nr, total_nr;
-   struct stat_data stat;
-};
-
-#define TODO_LIST_INIT { STRBUF_INIT }
-
-static void todo_list_release(struct todo_list *todo_list)
+void todo_list_release(struct todo_list *todo_list)
 {
strbuf_release(_list->buf);
FREE_AND_NULL(todo_list->items);
@@ -2060,7 +2015,7 @@ static int parse_insn_line(struct todo_item *item, const 
char *bol, char *eol)
return !item->commit;
 }
 
-static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list)
 {
struct todo_item *item;
char *p = buf, *next_p;
@@ -2158,7 +2113,7 @@ static int read_populate_todo(struct todo_list *todo_list,
return error(_("could not stat '%s'"), todo_file);
fill_stat_data(_list->stat, );
 
-   res = parse_insn_buffer(todo_list->buf.buf, todo_list);
+   res = todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
return error(_("please fix this using "
@@ -2189,7 +2144,7 @@ static int read_populate_todo(struct todo_list *todo_list,
FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
 
if (strbuf_read_file(, rebase_path_done(), 0) > 0 &&
-   !parse_insn_buffer(done.buf.buf, ))
+   !todo_list_parse_insn_buffer(done.buf.buf, 
))
todo_list->done_nr = count_commands();
else
todo_list->done_nr = 0;
@@ -4454,7 +4409,7 @@ int sequencer_add_exec_commands(const char *commands)
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error(_("could not read '%s'."), todo_file);
 
-   if (parse_insn_buffer(todo_list.buf.buf, _list)) {
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
return error(_("unusable todo list: '%s'"), todo_file);
}
@@ -4510,7 +4465,7 @@ int transform_todos(unsigned flags)
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error(_("could not read '%s'."), todo_file);
 
-   if (parse_insn_buffer(todo_list.buf.buf, _list)) {
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
return error(_("unusable todo list: '%s'"), todo_file);
}
@@ -4596,7 +4551,7 @@ int check_todo_list(void)
goto leave_check;
}
advise_to_edit_todo = res =
-   parse_insn_buffer(todo_list.buf.buf, _list);
+   todo_list_parse_insn_buffer(todo_list.buf.buf, _list);
 
if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
goto leave_check;
@@ -4615,7 +4570,7 @@ int check_todo_list(void)
goto leave_check;
}
strbuf_release(_file);
-   res = !!parse_insn_buffer(todo_list.buf.buf, _list);

[PATCH v3 09/16] sequencer: change complete_action() to use the refactored functions

2018-11-09 Thread Alban Gruin
complete_action() used functions that read the todo-list file, made some
changes to it, and wrote it back to the disk.

The previous commits were dedicated to separate the part that deals with
the file from the actual logic of these functions.  Now that this is
done, we can call directly the "logic" functions to avoid useless file
access.

The parsing of the list has to be done by the caller.  If the buffer of
the todo list provided by the caller is empty, a `noop' command is
directly added to the todo list, without touching to the buffer.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 16 ++-
 sequencer.c   | 80 +++
 sequencer.h   |  2 +-
 3 files changed, 40 insertions(+), 58 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index c740a7dd5d..99cbd1e8e3 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
struct argv_array make_script_args = ARGV_ARRAY_INIT;
-   FILE *todo_list_file;
struct todo_list todo_list = TODO_LIST_INIT;
 
if (prepare_branch_to_be_rebased(opts, switch_to))
@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   todo_list_file = fopen(rebase_path_todo(), "w");
-   if (!todo_list_file) {
-   free(revisions);
-   free(shortrevisions);
-
-   return error_errno(_("could not open %s"), rebase_path_todo());
-   }
-
argv_array_pushl(_script_args, "", revisions, NULL);
if (restrict_revision)
argv_array_push(_script_args, restrict_revision);
@@ -109,15 +100,16 @@ static int do_interactive_rebase(struct replay_opts 
*opts, unsigned flags,
ret = sequencer_make_script(_list.buf,
make_script_args.argc, 
make_script_args.argv,
flags);
-   fputs(todo_list.buf.buf, todo_list_file);
-   fclose(todo_list_file);
 
if (ret)
error(_("could not generate todo list"));
else {
discard_cache();
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
+   BUG("unusable todo list");
+
ret = complete_action(opts, flags, shortrevisions, onto_name, 
onto,
- head_hash, commands, autosquash);
+ head_hash, commands, autosquash, 
_list);
}
 
free(revisions);
diff --git a/sequencer.c b/sequencer.c
index 3389a753b6..64a99ab84f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4700,93 +4700,83 @@ static int skip_unnecessary_picks(struct object_id 
*output_oid)
return 0;
 }
 
+static int todo_list_rearrange_squash(struct todo_list *todo_list);
+
 int complete_action(struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
const char *onto, const char *orig_head, struct string_list 
*commands,
-   unsigned autosquash)
+   unsigned autosquash, struct todo_list *todo_list)
 {
const char *shortonto, *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   struct strbuf *buf = &(todo_list.buf);
+   struct todo_list new_todo = TODO_LIST_INIT;
+   struct strbuf *buf = _list->buf;
struct object_id oid;
-   struct stat st;
 
get_oid(onto, );
shortonto = find_unique_abbrev(, DEFAULT_ABBREV);
 
-   if (!lstat(todo_file, ) && st.st_size == 0 &&
-   write_message("noop\n", 5, todo_file, 0))
-   return -1;
+   if (buf->len == 0) {
+   struct todo_item *item = append_new_todo(todo_list);
+   item->command = TODO_NOOP;
+   item->commit = NULL;
+   item->arg = NULL;
+   item->arg_len = item->flags = item->offset_in_buf = 0;
+   }
 
-   if (autosquash && rearrange_squash_in_todo_file())
+   if (autosquash && todo_list_rearrange_squash(todo_list))
return -1;
 
if (commands->nr)
-   sequencer_add_exec_commands(commands);
-
-   if (strbuf_read_file(buf, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(buf->buf, _list)) {
-   todo_list_release(_list);
-   retur

[PATCH v3 01/16] sequencer: changes in parse_insn_buffer()

2018-11-09 Thread Alban Gruin
This clears the number of items of a todo_list before parsing it to
allow to parse the same list multiple times without issues.  As its
items are not dynamically allocated, or don’t need to allocate memory,
no additionnal memory management is required here.

Furthermore, if a line is invalid, the type of the corresponding
command is set to a garbage value, and its argument is defined properly.
This will allow to recreate the text of a todo list from its commands,
even if one of them is incorrect.

Signed-off-by: Alban Gruin 
---
No changes since v2.

 sequencer.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/sequencer.c b/sequencer.c
index 9e1ab3a2a7..07cc91d8db 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2066,6 +2066,8 @@ static int parse_insn_buffer(char *buf, struct todo_list 
*todo_list)
char *p = buf, *next_p;
int i, res = 0, fixup_okay = file_exists(rebase_path_done());
 
+   todo_list->current = todo_list->nr = 0;
+
for (i = 1; *p; i++, p = next_p) {
char *eol = strchrnul(p, '\n');
 
@@ -2079,7 +2081,10 @@ static int parse_insn_buffer(char *buf, struct todo_list 
*todo_list)
if (parse_insn_line(item, p, eol)) {
res = error(_("invalid line %d: %.*s"),
i, (int)(eol - p), p);
-   item->command = TODO_NOOP;
+   item->command = TODO_COMMENT + 1;
+   item->arg = p;
+   item->arg_len = (int)(eol - p);
+   item->commit = NULL;
}
 
if (fixup_okay)
-- 
2.19.1.872.ga867da739e



[PATCH v3 13/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit

2018-11-09 Thread Alban Gruin
edit_todo_list() is changed to work on a todo_list, and to handle the
initial edition of the todo list (ie. making a backup of the todo
list).

It does not check for dropped commits yet, as todo_list_check() does not
take the commits that have already been processed by the rebase (ie. the
todo list is edited in the middle of a rebase session).

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 23 -
 rebase-interactive.c  | 48 ++-
 rebase-interactive.h  |  4 ++-
 sequencer.c   |  3 +--
 sequencer.h   |  1 +
 5 files changed, 52 insertions(+), 27 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 99cbd1e8e3..6871990544 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -13,6 +13,27 @@ static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 
+static int edit_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT,
+   new_todo = TODO_LIST_INIT;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   strbuf_stripspace(_list.buf, 1);
+   if (!edit_todo_list(_list, _todo, NULL, NULL, flags) &&
+   todo_list_write_to_file(_todo, todo_file, NULL, NULL, -1,
+   flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)
+   return error_errno(_("could not write '%s'"), todo_file);
+
+   todo_list_release(_list);
+   todo_list_release(_todo);
+
+   return 0;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
   char **revisions, char **shortrevisions)
@@ -241,7 +262,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
break;
}
case EDIT_TODO:
-   ret = edit_todo_list(flags);
+   ret = edit_todo_file(flags);
break;
case SHOW_CURRENT_PATCH: {
struct child_process cmd = CHILD_PROCESS_INIT;
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 3b7b5e3382..e0fa88b90e 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int 
command_count,
}
 }
 
-int edit_todo_list(unsigned flags)
+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,
+  const char *shortrevisions, const char *shortonto,
+  unsigned flags)
 {
const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   int res = 0;
-
-   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   strbuf_stripspace(_list.buf, 1);
-   todo_list_parse_insn_buffer(todo_list.buf.buf, _list);
-   if (todo_list_write_to_file(_list, todo_file, NULL, NULL, -1,
-   flags | TODO_LIST_SHORTEN_IDS | 
TODO_LIST_APPEND_TODO_HELP)) {
-   todo_list_release(_list);
-   return -1;
+   unsigned initial = shortrevisions && shortonto;
+
+   if (initial) {
+   todo_list_write_to_file(todo_list, todo_file, shortrevisions, 
shortonto, -1,
+   flags | TODO_LIST_SHORTEN_IDS | 
TODO_LIST_APPEND_TODO_HELP);
+
+   if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
+   return error(_("could not copy '%s' to '%s'."), 
todo_file,
+rebase_path_todo_backup());
+   } else {
+   todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);
+   todo_list_write_to_file(todo_list, todo_file, NULL, NULL, -1,
+   flags | TODO_LIST_SHORTEN_IDS | 
TODO_LIST_APPEND_TODO_HELP);
}
 
-   strbuf_reset(_list.buf);
-   if (launch_sequence_editor(todo_file, _list.buf, NULL)) {
-   todo_list_release(_list);
-   return -1;
-   }
+   if (launch_sequence_editor(todo_file, _todo->buf, NULL))
+   return -2;
 
-   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
-   res = todo_list_write_to_file(_list, todo_file, NULL, 
NULL, -1,
- flags & ~(TODO_LIST_SHORTEN_IDS));
+   strbuf_stripspace(_todo->buf, 1);
+   if (initial && new_todo->buf.len == 0)
+   return -3;
 
-

[PATCH v3 08/16] sequencer: make sequencer_make_script() write its script to a strbuf

2018-11-09 Thread Alban Gruin
This makes sequencer_make_script() write its script to a strbuf (ie. the
buffer of a todo_list) instead of a FILE.  This reduce the amount of
read/write made by rebase interactive.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 13 +++-
 sequencer.c   | 38 ---
 sequencer.h   |  2 +-
 3 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index a6d83a684e..c740a7dd5d 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
struct argv_array make_script_args = ARGV_ARRAY_INIT;
-   FILE *todo_list;
+   FILE *todo_list_file;
+   struct todo_list todo_list = TODO_LIST_INIT;
 
if (prepare_branch_to_be_rebased(opts, switch_to))
return -1;
@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   todo_list = fopen(rebase_path_todo(), "w");
-   if (!todo_list) {
+   todo_list_file = fopen(rebase_path_todo(), "w");
+   if (!todo_list_file) {
free(revisions);
free(shortrevisions);
 
@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts 
*opts, unsigned flags,
if (restrict_revision)
argv_array_push(_script_args, restrict_revision);
 
-   ret = sequencer_make_script(todo_list,
+   ret = sequencer_make_script(_list.buf,
make_script_args.argc, 
make_script_args.argv,
flags);
-   fclose(todo_list);
+   fputs(todo_list.buf.buf, todo_list_file);
+   fclose(todo_list_file);
 
if (ret)
error(_("could not generate todo list"));
@@ -120,6 +122,7 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
 
free(revisions);
free(shortrevisions);
+   todo_list_release(_list);
argv_array_clear(_script_args);
 
return ret;
diff --git a/sequencer.c b/sequencer.c
index fce97e5f11..3389a753b6 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4109,7 +4109,7 @@ static const char *label_oid(struct object_id *oid, const 
char *label,
 }
 
 static int make_script_with_merges(struct pretty_print_context *pp,
-  struct rev_info *revs, FILE *out,
+  struct rev_info *revs, struct strbuf *out,
   unsigned flags)
 {
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
@@ -4254,7 +4254,7 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
 * gathering commits not yet shown, reversing the list on the fly,
 * then outputting that list (labeling revisions as needed).
 */
-   fprintf(out, "%s onto\n", cmd_label);
+   strbuf_addf(out, "%s onto\n", cmd_label);
for (iter = tips; iter; iter = iter->next) {
struct commit_list *list = NULL, *iter2;
 
@@ -4264,9 +4264,9 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
entry = oidmap_get(, >object.oid);
 
if (entry)
-   fprintf(out, "\n%c Branch %s\n", comment_line_char, 
entry->string);
+   strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, 
entry->string);
else
-   fprintf(out, "\n");
+   strbuf_addch(out, '\n');
 
while (oidset_contains(, >object.oid) &&
   !oidset_contains(, >object.oid)) {
@@ -4279,8 +4279,8 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
}
 
if (!commit)
-   fprintf(out, "%s %s\n", cmd_reset,
-   rebase_cousins ? "onto" : "[new root]");
+   strbuf_addf(out, "%s %s\n", cmd_reset,
+   rebase_cousins ? "onto" : "[new root]");
else {
const char *to = NULL;
 
@@ -4293,12 +4293,12 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
   );
 
if (!to || !strcmp(to, "onto"))
-   fprintf(out, "%s onto\n", cmd_reset);
+   strbuf_addf(out, "%s onto\n"

[PATCH v3 15/16] sequencer: fix a call to error() in transform_todo_file()

2018-11-09 Thread Alban Gruin
This replaces a call to error() by a call to error_errno() after writing
the content of the todo list to the disk in transform_todo_file().

Signed-off-by: Alban Gruin 
---
No changes since v2.

 sequencer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sequencer.c b/sequencer.c
index 02afd2f5cd..a55df3526f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4464,7 +4464,7 @@ int sequencer_add_exec_commands(struct string_list 
*commands)
int res;
 
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
+   return error_errno(_("could not read '%s'."), todo_file);
 
if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
-- 
2.19.1.872.ga867da739e



[PATCH v3 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c

2018-11-09 Thread Alban Gruin
As transform_todo_file() is only needed inside of rebase--interactive.c,
it is moved there from sequencer.c.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 20 
 sequencer.c   | 20 
 sequencer.h   |  1 -
 3 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 6871990544..580c6a3822 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -34,6 +34,26 @@ static int edit_todo_file(unsigned flags)
return 0;
 }
 
+static int transform_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_release(_list);
+   return error(_("unusable todo list: '%s'"), todo_file);
+   }
+
+   res = todo_list_write_to_file(_list, todo_file,
+ NULL, NULL, -1, flags);
+   todo_list_release(_list);
+   return res;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
   char **revisions, char **shortrevisions)
diff --git a/sequencer.c b/sequencer.c
index a55df3526f..896dd04150 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4542,26 +4542,6 @@ int todo_list_write_to_file(struct todo_list *todo_list, 
const char *file,
return res;
 }
 
-int transform_todo_file(unsigned flags)
-{
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   int res;
-
-   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
-   res = todo_list_write_to_file(_list, todo_file,
- NULL, NULL, -1, flags);
-   todo_list_release(_list);
-   return res;
-}
-
 static const char edit_todo_list_advice[] =
 N_("You can fix this with 'git rebase --edit-todo' "
 "and then run 'git rebase --continue'.\n"
diff --git a/sequencer.h b/sequencer.h
index f6751d53b9..ee59233344 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -142,7 +142,6 @@ int sequencer_make_script(struct strbuf *out, int argc, 
const char **argv,
  unsigned flags);
 
 int sequencer_add_exec_commands(struct string_list *commands);
-int transform_todo_file(unsigned flags);
 int check_todo_list_from_file(void);
 int complete_action(struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
-- 
2.19.1.872.ga867da739e



[PATCH v3 10/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list

2018-11-09 Thread Alban Gruin
This refactors skip_unnecessary_picks() to work on a todo_list.  The
file-handling logic is completely dropped here, as its only usage is
made by complete_action().

Instead of truncating the todo list’s buffer, the items are moved to
the beginning of the list, eliminating the need to reparse the list.
This also means its buffer cannot be directly written to the disk.

rewrite_file() is then removed, as it is now unused.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 79 -
 1 file changed, 17 insertions(+), 62 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 64a99ab84f..1c405763c3 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4607,52 +4607,21 @@ int check_todo_list_from_file(void)
return res;
 }
 
-static int rewrite_file(const char *path, const char *buf, size_t len)
-{
-   int rc = 0;
-   int fd = open(path, O_WRONLY | O_TRUNC);
-   if (fd < 0)
-   return error_errno(_("could not open '%s' for writing"), path);
-   if (write_in_full(fd, buf, len) < 0)
-   rc = error_errno(_("could not write to '%s'"), path);
-   if (close(fd) && !rc)
-   rc = error_errno(_("could not close '%s'"), path);
-   return rc;
-}
-
 /* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct object_id *output_oid)
+static int skip_unnecessary_picks(struct todo_list *todo_list,
+ struct object_id *output_oid)
 {
-   const char *todo_file = rebase_path_todo();
-   struct strbuf buf = STRBUF_INIT;
-   struct todo_list todo_list = TODO_LIST_INIT;
struct object_id *parent_oid;
-   int fd, i;
-
-   if (!read_oneliner(, rebase_path_onto(), 0))
-   return error(_("could not read 'onto'"));
-   if (get_oid(buf.buf, output_oid)) {
-   strbuf_release();
-   return error(_("need a HEAD to fixup"));
-   }
-   strbuf_release();
-
-   if (strbuf_read_file_or_whine(_list.buf, todo_file) < 0)
-   return -1;
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
+   int i;
 
-   for (i = 0; i < todo_list.nr; i++) {
-   struct todo_item *item = todo_list.items + i;
+   for (i = 0; i < todo_list->nr; i++) {
+   struct todo_item *item = todo_list->items + i;
 
if (item->command >= TODO_NOOP)
continue;
if (item->command != TODO_PICK)
break;
if (parse_commit(item->commit)) {
-   todo_list_release(_list);
return error(_("could not parse commit '%s'"),
oid_to_hex(>commit->object.oid));
}
@@ -4666,37 +4635,21 @@ static int skip_unnecessary_picks(struct object_id 
*output_oid)
oidcpy(output_oid, >commit->object.oid);
}
if (i > 0) {
-   int offset = get_item_line_offset(_list, i);
const char *done_path = rebase_path_done();
 
-   fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
-   if (fd < 0) {
-   error_errno(_("could not open '%s' for writing"),
-   done_path);
-   todo_list_release(_list);
-   return -1;
-   }
-   if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+   if (todo_list_write_to_file(todo_list, done_path, NULL, NULL, 
i, 0)) {
error_errno(_("could not write to '%s'"), done_path);
-   todo_list_release(_list);
-   close(fd);
return -1;
}
-   close(fd);
 
-   if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
-todo_list.buf.len - offset) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
+   MOVE_ARRAY(todo_list->items, todo_list->items + i, 
todo_list->nr - i);
+   todo_list->nr -= i;
+   todo_list->current = 0;
 
-   todo_list.current = i;
-   if (is_fixup(peek_command(_list, 0)))
-   record_in_rewritten(output_oid, 
peek_command(_list, 0));
+   if (is_fixup(peek_command(todo_list, 0)))
+   record_in_rewritten(output_oid, peek_command(todo_list, 
0));
}
 
-   todo_list_release(_list);
-
return 0;
 }
 
@@ -4770,6 +4723,11 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
   

[PATCH v3 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list

2018-11-09 Thread Alban Gruin
This refactors sequencer_add_exec_commands() to work on a todo_list to
avoid redundant reads and writes to the disk.

Instead of just inserting the `exec' command between the other commands,
and re-parsing the buffer at the end the exec command is appended to the
buffer once, and a new list of items is created.  Items from the old
list are copied across and new `exec' items are appended when
necessary.  This eliminates the need to reparse the buffer, but this
also means we have to use todo_list_write_to_disk() to write the file().

todo_list_add_exec_commands() and sequencer_add_exec_commands() are
modified to take a string list instead of a string -- one item for each
command.  This makes it easier to insert a new command to the todo list
for each command to execute.

sequencer_add_exec_commands() still reads the todo list from the disk,
as it is needed by rebase -p.

complete_action() still uses sequencer_add_exec_commands() for now.
This will be changed in a future commit.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |  15 +++--
 sequencer.c   | 111 +-
 sequencer.h   |   4 +-
 3 files changed, 83 insertions(+), 47 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index c1d87c0fe6..1fb5a43c0d 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
 const char *onto, const char *onto_name,
 const char *squash_onto, const char *head_name,
 const char *restrict_revision, char 
*raw_strategies,
-const char *cmd, unsigned autosquash)
+struct string_list *commands, unsigned 
autosquash)
 {
int ret;
const char *head_hash = NULL;
@@ -115,7 +115,7 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
else {
discard_cache();
ret = complete_action(opts, flags, shortrevisions, onto_name, 
onto,
- head_hash, cmd, autosquash);
+ head_hash, commands, autosquash);
}
 
free(revisions);
@@ -138,6 +138,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
*squash_onto = NULL, *upstream = NULL, *head_name = NULL,
*switch_to = NULL, *cmd = NULL;
+   struct string_list commands = STRING_LIST_INIT_DUP;
char *raw_strategies = NULL;
enum {
NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,
@@ -220,6 +221,12 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
warning(_("--[no-]rebase-cousins has no effect without "
  "--rebase-merges"));
 
+   if (cmd && *cmd) {
+   string_list_split(, cmd, '\n', -1);
+   if (strlen(commands.items[commands.nr - 1].string) == 0)
+   --commands.nr;
+   }
+
switch (command) {
case NONE:
if (!onto && !upstream)
@@ -227,7 +234,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
 
ret = do_interactive_rebase(, flags, switch_to, upstream, 
onto,
onto_name, squash_onto, head_name, 
restrict_revision,
-   raw_strategies, cmd, autosquash);
+   raw_strategies, , 
autosquash);
break;
case SKIP: {
struct string_list merge_rr = STRING_LIST_INIT_DUP;
@@ -261,7 +268,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
ret = rearrange_squash();
break;
case ADD_EXEC:
-   ret = sequencer_add_exec_commands(cmd);
+   ret = sequencer_add_exec_commands();
break;
default:
BUG("invalid command '%d'", command);
diff --git a/sequencer.c b/sequencer.c
index 900899ef20..11692d0b98 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4394,24 +4394,29 @@ int sequencer_make_script(FILE *out, int argc, const 
char **argv,
return 0;
 }
 
-/*
- * Add commands after pick and (series of) squash/fixup commands
- * in the todo list.
- */
-int sequencer_add_exec_commands(const char *commands)
+static void todo_list_add_exec_commands(struct todo_list *todo_list,
+   struct string_list *commands)
 {
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   struct strb

[PATCH v3 05/16] sequencer: refactor check_todo_list() to work on a todo_list

2018-11-09 Thread Alban Gruin
This refactors check_todo_list() to work on a todo_list to avoid
redundant reads and writes to the disk.  The function is renamed
todo_list_check().  The parsing of the two todo lists is left to the
caller.

As rebase -p still need to check the todo list from the disk, a new
function is introduced, check_todo_list_from_file().  It reads the file
from the disk, parses it, pass the todo_list to todo_list_check(), and
writes it back to the disk.

As get_missing_commit_check_level() and the enum
missing_commit_check_level are no longer needed inside of sequencer.c,
they are moved to rebase-interactive.c, and made static again.

Signed-off-by: Alban Gruin 
---
No changes since v2.

 builtin/rebase--interactive.c |   2 +-
 rebase-interactive.c  |  90 -
 rebase-interactive.h  |   1 +
 sequencer.c   | 120 +++---
 sequencer.h   |   9 +--
 5 files changed, 115 insertions(+), 107 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index abdf6126df..c1d87c0fe6 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -255,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
ret = transform_todo_file(flags);
break;
case CHECK_TODO_LIST:
-   ret = check_todo_list();
+   ret = check_todo_list_from_file();
break;
case REARRANGE_SQUASH:
ret = rearrange_squash();
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 4cd487a450..3adcf39e07 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -1,8 +1,32 @@
 #include "cache.h"
 #include "commit.h"
-#include "rebase-interactive.h"
 #include "sequencer.h"
+#include "rebase-interactive.h"
 #include "strbuf.h"
+#include "commit-slab.h"
+#include "config.h"
+
+enum missing_commit_check_level {
+   MISSING_COMMIT_CHECK_IGNORE = 0,
+   MISSING_COMMIT_CHECK_WARN,
+   MISSING_COMMIT_CHECK_ERROR
+};
+
+static enum missing_commit_check_level get_missing_commit_check_level(void)
+{
+   const char *value;
+
+   if (git_config_get_value("rebase.missingcommitscheck", ) ||
+   !strcasecmp("ignore", value))
+   return MISSING_COMMIT_CHECK_IGNORE;
+   if (!strcasecmp("warn", value))
+   return MISSING_COMMIT_CHECK_WARN;
+   if (!strcasecmp("error", value))
+   return MISSING_COMMIT_CHECK_ERROR;
+   warning(_("unrecognized setting %s for option "
+ "rebase.missingCommitsCheck. Ignoring."), value);
+   return MISSING_COMMIT_CHECK_IGNORE;
+}
 
 void append_todo_help(unsigned edit_todo, unsigned keep_empty,
  struct strbuf *buf)
@@ -89,3 +113,67 @@ int edit_todo_list(unsigned flags)
 
return 0;
 }
+
+define_commit_slab(commit_seen, unsigned char);
+/*
+ * Check if the user dropped some commits by mistake
+ * Behaviour determined by rebase.missingCommitsCheck.
+ * Check if there is an unrecognized command or a
+ * bad SHA-1 in a command.
+ */
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
+{
+   enum missing_commit_check_level check_level = 
get_missing_commit_check_level();
+   struct strbuf missing = STRBUF_INIT;
+   int res = 0, i;
+   struct commit_seen commit_seen;
+
+   init_commit_seen(_seen);
+
+   if (check_level == MISSING_COMMIT_CHECK_IGNORE)
+   goto leave_check;
+
+   /* Mark the commits in git-rebase-todo as seen */
+   for (i = 0; i < new_todo->nr; i++) {
+   struct commit *commit = new_todo->items[i].commit;
+   if (commit)
+   *commit_seen_at(_seen, commit) = 1;
+   }
+
+   /* Find commits in git-rebase-todo.backup yet unseen */
+   for (i = old_todo->nr - 1; i >= 0; i--) {
+   struct todo_item *item = old_todo->items + i;
+   struct commit *commit = item->commit;
+   if (commit && !*commit_seen_at(_seen, commit)) {
+   strbuf_addf(, " - %s %.*s\n",
+   find_unique_abbrev(>object.oid, 
DEFAULT_ABBREV),
+   item->arg_len, item->arg);
+   *commit_seen_at(_seen, commit) = 1;
+   }
+   }
+
+   /* Warn about missing commits */
+   if (!missing.len)
+   goto leave_check;
+
+   if (check_level == MISSING_COMMIT_CHECK_ERROR)
+   res = 1;
+
+   fprintf(stderr,
+   _("Warning: some commits may have been dropped accidentally.\n"
+   "Dropped commits (newer to older):\n"));
+
+   /* Make the l

[PATCH v3 03/16] sequencer: refactor transform_todos() to work on a todo_list

2018-11-09 Thread Alban Gruin
This refactors transform_todos() to work on a todo_list.  The function
is renamed todo_list_transform().

As rebase -p still need to check the todo list from the disk, a new
function is introduced, transform_todo_file().  It is still used by
complete_action() and edit_todo_list() for now, but they will be
replaced in a future commit.

todo_list_transform() is not a static function, because it will be used
by edit_todo_list() from rebase-interactive.c in a future commit.

Signed-off-by: Alban Gruin 
---
No changes since v2.

 builtin/rebase--interactive.c |  2 +-
 rebase-interactive.c  |  4 +--
 sequencer.c   | 46 +++
 sequencer.h   |  3 ++-
 4 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index a2ab68ed06..abdf6126df 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -252,7 +252,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
}
case SHORTEN_OIDS:
case EXPAND_OIDS:
-   ret = transform_todos(flags);
+   ret = transform_todo_file(flags);
break;
case CHECK_TODO_LIST:
ret = check_todo_list();
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 78f3263fc1..4cd487a450 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -69,7 +69,7 @@ int edit_todo_list(unsigned flags)
 
strbuf_release();
 
-   transform_todos(flags | TODO_LIST_SHORTEN_IDS);
+   transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);
 
if (strbuf_read_file(, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
@@ -85,7 +85,7 @@ int edit_todo_list(unsigned flags)
if (launch_sequence_editor(todo_file, NULL, NULL))
return -1;
 
-   transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));
+   transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));
 
return 0;
 }
diff --git a/sequencer.c b/sequencer.c
index 7adbeaa27d..e1097660ed 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4454,23 +4454,13 @@ int sequencer_add_exec_commands(const char *commands)
return i;
 }
 
-int transform_todos(unsigned flags)
+void todo_list_transform(struct todo_list *todo_list, 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(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
-   for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+   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(, "%.*s\n", item->arg_len, item->arg);
@@ -4506,9 +4496,33 @@ int transform_todos(unsigned flags)
strbuf_addf(, " %.*s\n", item->arg_len, item->arg);
}
 
-   i = write_message(buf.buf, buf.len, todo_file, 0);
+   strbuf_reset(_list->buf);
+   strbuf_add(_list->buf, buf.buf, buf.len);
+   strbuf_release();
+
+   if (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))
+   BUG("unusable todo list");
+}
+
+int transform_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error(_("could not read '%s'."), todo_file);
+
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_release(_list);
+   return error(_("unusable todo list: '%s'"), todo_file);
+   }
+
+   todo_list_transform(_list, flags);
+
+   res = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);
todo_list_release(_list);
-   return i;
+   return res;
 }
 
 enum missing_commit_check_level get_missing_commit_check_level(void)
@@ -4769,7 +4783,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
return error(_("could not copy '%s' to '%s'."), todo_file,
 rebase_path_todo_backup());
 
-   if (transform_todos(flags | TODO_LIST_SHORTEN_IDS))
+   if (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))
return error(_("

[PATCH v3 12/16] rebase-interactive: append_todo_help() changes

2018-11-09 Thread Alban Gruin
This moves the writing of the comment "Rebase $shortrevisions onto
$shortonto ($command_count commands)" from complete_action() to
append_todo_help().

shortrevisions, shortonto, and command_count are passed as parameters to
append_todo_help().

During the initial edit of the todo list, shortrevisions and shortonto
are not NULL.  Therefore, if shortrevisions or shortonto is NULL, then
edit_todo would be true, otherwise it would be false.  Thus, edit_todo
is removed from the parameters of append_todo_help().

edit_todo_list() and complete_action() are modified to fit these
changes.

Signed-off-by: Alban Gruin 
---
 rebase-interactive.c | 12 +++-
 rebase-interactive.h |  3 ++-
 sequencer.c  | 17 -
 3 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/rebase-interactive.c b/rebase-interactive.c
index 23569cfa3c..3b7b5e3382 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -28,7 +28,8 @@ static enum missing_commit_check_level 
get_missing_commit_check_level(void)
return MISSING_COMMIT_CHECK_IGNORE;
 }
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
  struct strbuf *buf)
 {
const char *msg = _("\nCommands:\n"
@@ -48,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned 
keep_empty,
 ".   specified). Use -c  to reword the commit message.\n"
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
+   unsigned edit_todo = !(shortrevisions && shortonto);
+
+   if (!edit_todo) {
+   strbuf_addch(buf, '\n');
+   strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+ "Rebase %s onto %s (%d commands)",
+ command_count),
+ shortrevisions, shortonto, command_count);
+   }
 
strbuf_add_commented_lines(buf, msg, strlen(msg));
 
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 6bc7bc315d..61858f3a02 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -1,7 +1,8 @@
 #ifndef REBASE_INTERACTIVE_H
 #define REBASE_INTERACTIVE_H
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
  struct strbuf *buf);
 int edit_todo_list(unsigned flags);
 int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
diff --git a/sequencer.c b/sequencer.c
index 1c405763c3..cf6f69c93e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4529,22 +4529,13 @@ int todo_list_write_to_file(struct todo_list 
*todo_list, const char *file,
const char *shortrevisions, const char *shortonto,
int num, unsigned flags)
 {
-   int edit_todo = !(shortrevisions && shortonto), res;
+   int res;
struct strbuf buf = STRBUF_INIT;
 
todo_list_to_strbuf(todo_list, , num, flags);
-
-   if (flags & TODO_LIST_APPEND_TODO_HELP) {
-   int command_count = count_commands(todo_list);
-   if (!edit_todo) {
-   strbuf_addch(, '\n');
-   strbuf_commented_addf(, Q_("Rebase %s onto %s (%d 
command)",
-  "Rebase %s onto %s (%d 
commands)",
-  command_count),
- shortrevisions, shortonto, 
command_count);
-   }
-   append_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, );
-   }
+   if (flags & TODO_LIST_APPEND_TODO_HELP)
+   append_todo_help(flags & TODO_LIST_KEEP_EMPTY, 
count_commands(todo_list),
+shortrevisions, shortonto, );
 
res = write_message(buf.buf, buf.len, file, 0);
strbuf_release();
-- 
2.19.1.872.ga867da739e



[PATCH v3 11/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()

2018-11-09 Thread Alban Gruin
Just like complete_action(), edit_todo_list() used a
function (transform_todo_file()) that read the todo list from the disk
and wrote it back, resulting in useless disk accesses.

This changes edit_todo_list() to call directly todo_list_write_to_file()
instead.

Signed-off-by: Alban Gruin 
---
 rebase-interactive.c | 38 --
 1 file changed, 16 insertions(+), 22 deletions(-)

diff --git a/rebase-interactive.c b/rebase-interactive.c
index 3adcf39e07..23569cfa3c 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -79,39 +79,33 @@ void append_todo_help(unsigned edit_todo, unsigned 
keep_empty,
 
 int edit_todo_list(unsigned flags)
 {
-   struct strbuf buf = STRBUF_INIT;
const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res = 0;
 
-   if (strbuf_read_file(, todo_file, 0) < 0)
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
 
-   strbuf_stripspace(, 1);
-   if (write_message(buf.buf, buf.len, todo_file, 0)) {
-   strbuf_release();
+   strbuf_stripspace(_list.buf, 1);
+   todo_list_parse_insn_buffer(todo_list.buf.buf, _list);
+   if (todo_list_write_to_file(_list, todo_file, NULL, NULL, -1,
+   flags | TODO_LIST_SHORTEN_IDS | 
TODO_LIST_APPEND_TODO_HELP)) {
+   todo_list_release(_list);
return -1;
}
 
-   strbuf_release();
-
-   transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);
-
-   if (strbuf_read_file(, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   append_todo_help(1, 0, );
-   if (write_message(buf.buf, buf.len, todo_file, 0)) {
-   strbuf_release();
+   strbuf_reset(_list.buf);
+   if (launch_sequence_editor(todo_file, _list.buf, NULL)) {
+   todo_list_release(_list);
return -1;
}
 
-   strbuf_release();
+   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
+   res = todo_list_write_to_file(_list, todo_file, NULL, 
NULL, -1,
+ flags & ~(TODO_LIST_SHORTEN_IDS));
 
-   if (launch_sequence_editor(todo_file, NULL, NULL))
-   return -1;
-
-   transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));
-
-   return 0;
+   todo_list_release(_list);
+   return res;
 }
 
 define_commit_slab(commit_seen, unsigned char);
-- 
2.19.1.872.ga867da739e



[PATCH v3 07/16] sequencer: refactor rearrange_squash() to work on a todo_list

2018-11-09 Thread Alban Gruin
This refactors rearrange_squash() to work on a todo_list to avoid
redundant reads and writes.  The function is renamed
todo_list_rearrange_squash().

The old version created a new buffer, which was directly written to the
disk.  This new version creates a new item list by just memcpying items
from the old item list, without creating a new buffer.  This eliminates
the need to reparse the todo list, but this also means its buffer cannot
be directly written to the disk.

As rebase -p still need to check the todo list from the disk, a new
function is introduced, rearrange_squash_in_todo_file().

complete_action() still uses rearrange_squash_in_todo_file() for now.
This will be changed in a future commit.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |  2 +-
 sequencer.c   | 87 +--
 sequencer.h   |  2 +-
 3 files changed, 45 insertions(+), 46 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 1fb5a43c0d..a6d83a684e 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -265,7 +265,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
ret = check_todo_list_from_file();
break;
case REARRANGE_SQUASH:
-   ret = rearrange_squash();
+   ret = rearrange_squash_in_todo_file();
break;
case ADD_EXEC:
ret = sequencer_add_exec_commands();
diff --git a/sequencer.c b/sequencer.c
index 11692d0b98..fce97e5f11 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4722,7 +4722,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
write_message("noop\n", 5, todo_file, 0))
return -1;
 
-   if (autosquash && rearrange_squash())
+   if (autosquash && rearrange_squash_in_todo_file())
return -1;
 
if (commands->nr)
@@ -4828,21 +4828,13 @@ define_commit_slab(commit_todo_item, struct todo_item 
*);
  * message will have to be retrieved from the commit (as the oneline in the
  * script cannot be trusted) in order to normalize the autosquash arrangement.
  */
-int rearrange_squash(void)
+static int todo_list_rearrange_squash(struct todo_list *todo_list)
 {
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
struct hashmap subject2item;
-   int res = 0, rearranged = 0, *next, *tail, i;
+   int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
char **subjects;
struct commit_todo_item commit_todo;
-
-   if (strbuf_read_file_or_whine(_list.buf, todo_file) < 0)
-   return -1;
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
+   struct todo_item *items = NULL;
 
init_commit_todo_item(_todo);
/*
@@ -4855,13 +4847,13 @@ int rearrange_squash(void)
 * be moved to appear after the i'th.
 */
hashmap_init(, (hashmap_cmp_fn) subject2item_cmp,
-NULL, todo_list.nr);
-   ALLOC_ARRAY(next, todo_list.nr);
-   ALLOC_ARRAY(tail, todo_list.nr);
-   ALLOC_ARRAY(subjects, todo_list.nr);
-   for (i = 0; i < todo_list.nr; i++) {
+NULL, todo_list->nr);
+   ALLOC_ARRAY(next, todo_list->nr);
+   ALLOC_ARRAY(tail, todo_list->nr);
+   ALLOC_ARRAY(subjects, todo_list->nr);
+   for (i = 0; i < todo_list->nr; i++) {
struct strbuf buf = STRBUF_INIT;
-   struct todo_item *item = todo_list.items + i;
+   struct todo_item *item = todo_list->items + i;
const char *commit_buffer, *subject, *p;
size_t subject_len;
int i2 = -1;
@@ -4874,7 +4866,6 @@ int rearrange_squash(void)
}
 
if (is_fixup(item->command)) {
-   todo_list_release(_list);
clear_commit_todo_item(_todo);
return error(_("the script was already rearranged."));
}
@@ -4909,7 +4900,7 @@ int rearrange_squash(void)
 *commit_todo_item_at(_todo, commit2))
/* found by commit name */
i2 = *commit_todo_item_at(_todo, commit2)
-   - todo_list.items;
+   - todo_list->items;
else {
/* copy can be a prefix of the commit subject */
for (i2 = 0; i2 < i; i2++)
@@ -4922,7 +4913,7 @@ int rearrange_squash(void)
}
if (i2 >= 0) {
rearranged = 1;
-  

[PATCH v3 04/16] sequencer: introduce todo_list_write_to_file()

2018-11-09 Thread Alban Gruin
This introduces a new function to recreate the text of a todo list from
its commands and write it to a file.  This will be useful as the next
few commits will change the use of the buffer in struct todo_list so it
will no longer be a mirror of the file on disk.

This functionality can already be found in todo_list_transform(), but it
is specifically made to replace the buffer of a todo list, which is not
the desired behaviour.  Thus, the part of todo_list_transform() that
actually creates the buffer is moved to a new function,
todo_list_to_strbuf().  The rest is unused, and so is dropped.

todo_list_write_to_file() can also take care of appending the help text
to the buffer before writing it to the disk, or to write only the first
n items of the list.  This feature will be used by
skip_unnecessary_picks(), which has to write done commands in a file.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 60 -
 sequencer.h |  6 +-
 2 files changed, 46 insertions(+), 20 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index e1097660ed..19f24825d4 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4454,24 +4454,27 @@ int sequencer_add_exec_commands(const char *commands)
return i;
 }
 
-void todo_list_transform(struct todo_list *todo_list, unsigned flags)
+static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf 
*buf,
+   int num, unsigned flags)
 {
-   struct strbuf buf = STRBUF_INIT;
struct todo_item *item;
-   int i;
+   int i, max = todo_list->nr;
 
-   for (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {
+   if (num > 0 && num < max)
+   max = num;
+
+   for (item = todo_list->items, i = 0; i < max; i++, item++) {
/* if the item is not a command write it and continue */
if (item->command >= TODO_COMMENT) {
-   strbuf_addf(, "%.*s\n", item->arg_len, item->arg);
+   strbuf_addf(buf, "%.*s\n", item->arg_len, item->arg);
continue;
}
 
/* add command to the buffer */
if (flags & TODO_LIST_ABBREVIATE_CMDS)
-   strbuf_addch(, command_to_char(item->command));
+   strbuf_addch(buf, command_to_char(item->command));
else
-   strbuf_addstr(, command_to_string(item->command));
+   strbuf_addstr(buf, command_to_string(item->command));
 
/* add commit id */
if (item->commit) {
@@ -4481,27 +4484,47 @@ void todo_list_transform(struct todo_list *todo_list, 
unsigned flags)
 
if (item->command == TODO_MERGE) {
if (item->flags & TODO_EDIT_MERGE_MSG)
-   strbuf_addstr(, " -c");
+   strbuf_addstr(buf, " -c");
else
-   strbuf_addstr(, " -C");
+   strbuf_addstr(buf, " -C");
}
 
-   strbuf_addf(, " %s", oid);
+   strbuf_addf(buf, " %s", oid);
}
 
/* add all the rest */
if (!item->arg_len)
-   strbuf_addch(, '\n');
+   strbuf_addch(buf, '\n');
else
-   strbuf_addf(, " %.*s\n", item->arg_len, item->arg);
+   strbuf_addf(buf, " %.*s\n", item->arg_len, item->arg);
}
+}
 
-   strbuf_reset(_list->buf);
-   strbuf_add(_list->buf, buf.buf, buf.len);
+int todo_list_write_to_file(struct todo_list *todo_list, const char *file,
+   const char *shortrevisions, const char *shortonto,
+   int num, unsigned flags)
+{
+   int edit_todo = !(shortrevisions && shortonto), res;
+   struct strbuf buf = STRBUF_INIT;
+
+   todo_list_to_strbuf(todo_list, , num, flags);
+
+   if (flags & TODO_LIST_APPEND_TODO_HELP) {
+   int command_count = count_commands(todo_list);
+   if (!edit_todo) {
+   strbuf_addch(, '\n');
+   strbuf_commented_addf(, Q_("Rebase %s onto %s (%d 
command)",
+  "Rebase %s onto %s (%d 
commands)",
+  command_count),
+ shortrevisions, shortonto, 
command_count);
+   }
+   append_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMP

[PATCH v3 14/16] sequencer: use edit_todo_list() in complete_action()

2018-11-09 Thread Alban Gruin
This changes complete_action() to use edit_todo_list(), now that it can
handle the initial edit of the todo list.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 20 ++--
 1 file changed, 6 insertions(+), 14 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index f04b002e37..02afd2f5cd 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4654,6 +4654,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
struct todo_list new_todo = TODO_LIST_INIT;
struct strbuf *buf = _list->buf;
struct object_id oid;
+   int res;
 
get_oid(onto, );
shortonto = find_unique_abbrev(, DEFAULT_ABBREV);
@@ -4679,24 +4680,15 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
return error(_("nothing to do"));
}
 
-   if (todo_list_write_to_file(todo_list, todo_file,
-   shortrevisions, shortonto, -1,
-   flags | TODO_LIST_SHORTEN_IDS | 
TODO_LIST_APPEND_TODO_HELP))
-   return error_errno(_("could not write '%s'"), todo_file);
-
-   if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
-   return error(_("could not copy '%s' to '%s'."), todo_file,
-rebase_path_todo_backup());
-
-   if (launch_sequence_editor(todo_file, _todo.buf, NULL)) {
+   res = edit_todo_list(todo_list, _todo, shortrevisions, shortonto, 
flags);
+   if (res == -1)
+   return -1;
+   else if (res == -2) {
apply_autostash(opts);
sequencer_remove_state(opts);
 
return -1;
-   }
-
-   strbuf_stripspace(_todo.buf, 1);
-   if (new_todo.buf.len == 0) {
+   } else if (res == -3) {
apply_autostash(opts);
sequencer_remove_state(opts);
todo_list_release(_todo);
-- 
2.19.1.872.ga867da739e



[PATCH v3 00/16] sequencer: refactor functions working on a todo_list

2018-11-09 Thread Alban Gruin
At the center of the "interactive" part of the interactive rebase lies
the todo list.  When the user starts an interactive rebase, a todo list
is generated, presented to the user (who then edits it using a text
editor), read back, and then is checked and processed before the actual
rebase takes place.

Some of this processing includes adding execs commands, reordering
fixup! and squash! commits, and checking if no commits were accidentally
dropped by the user.

Before I converted the interactive rebase in C, these functions were
called by git-rebase--interactive.sh through git-rebase--helper.  Since
the only way to pass around a large amount of data between a shell
script and a C program is to use a file (or any declination of a file),
the functions that checked and processed the todo list were directly
working on a file, the same file that the user edited.

During the conversion, I did not address this issue, which lead to a
complete_action() that reads the todo list file, does some computation
based on its content, and writes it back to the disk, several times in
the same function.

As it is not an efficient way to handle a data structure, this patch
series refactor the functions that processes the todo list to work on a
todo_list structure instead of reading it from the disk.

Some commits consists in modifying edit_todo_list() (initially used by
--edit-todo) to handle the initial edition of the todo list, to increase
code sharing.

This is based on 8858448bb4 ("Ninth batch for 2.20", 2018-11-06) to
avoid conflicts with 'js/rebase-i-break'.

Changes since v2:

 - Clarifying some commit messages

 - Reducing the number of parameters in todo_list_write_to_file()

 - sequencer_add_exec_commands() now requests a string list instead of a
   string.

 - Replacing calls to memcpy() by shallow copies

 - Applying array.cocci.patch

Alban Gruin (16):
  sequencer: changes in parse_insn_buffer()
  sequencer: make the todo_list structure public
  sequencer: refactor transform_todos() to work on a todo_list
  sequencer: introduce todo_list_write_to_file()
  sequencer: refactor check_todo_list() to work on a todo_list
  sequencer: refactor sequencer_add_exec_commands() to work on a
todo_list
  sequencer: refactor rearrange_squash() to work on a todo_list
  sequencer: make sequencer_make_script() write its script to a strbuf
  sequencer: change complete_action() to use the refactored functions
  sequencer: refactor skip_unnecessary_picks() to work on a todo_list
  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()
  rebase-interactive: append_todo_help() changes
  rebase-interactive: rewrite edit_todo_list() to handle the initial
edit
  sequencer: use edit_todo_list() in complete_action()
  sequencer: fix a call to error() in transform_todo_file()
  rebase--interactive: move transform_todo_file() to
rebase--interactive.c

 builtin/rebase--interactive.c |  81 +++--
 rebase-interactive.c  | 142 ++--
 rebase-interactive.h  |   8 +-
 sequencer.c   | 615 +-
 sequencer.h   |  75 -
 5 files changed, 481 insertions(+), 440 deletions(-)

Range-diff against v2:
 1:  55fa1fff03 =  1:  9ae965b73e sequencer: changes in parse_insn_buffer()
 2:  192fb771ed <  -:  -- sequencer: make the todo_list structure public
 -:  -- >  2:  9c15cdede4 sequencer: make the todo_list structure public
 3:  e2f821ffbe =  3:  a5c01d5a95 sequencer: refactor transform_todos() to work 
on a todo_list
 4:  0001e8dbde <  -:  -- sequencer: introduce todo_list_write_to_file()
 -:  -- >  4:  f2781fe4c3 sequencer: introduce todo_list_write_to_file()
 5:  aa9554d81d =  5:  337e0dce57 sequencer: refactor check_todo_list() to work 
on a todo_list
 6:  8bd793caf5 <  -:  -- sequencer: refactor 
sequencer_add_exec_commands() to work on a todo_list
 7:  e984946cef <  -:  -- sequencer: refactor rearrange_squash() to 
work on a todo_list
 8:  cf05254acf <  -:  -- sequencer: make sequencer_make_script() write 
its script to a strbuf
 9:  ac79151792 <  -:  -- sequencer: change complete_action() to use 
the refactored functions
10:  e227e38b24 <  -:  -- sequencer: refactor skip_unnecessary_picks() 
to work on a todo_list
11:  5bfd9d3460 <  -:  -- rebase-interactive: use 
todo_list_write_to_file() in edit_todo_list()
12:  f4d7578a77 <  -:  -- rebase-interactive: append_todo_help() changes
13:  11c43f1dfe <  -:  -- rebase-interactive: rewrite edit_todo_list() 
to handle the initial edit
14:  e9a6396c26 <  -:  -- sequencer: use edit_todo_list() in 
complete_action()
 -:  -- >  6:  2c75eed153 sequencer: refactor 
sequencer_add_exec_commands() to work on a todo_list
 -:  -- >  7:  90e01bc713 sequencer: refactor rearrange_squash() to 
work on a todo_list
 -:  -- >

Re: [PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list

2018-11-02 Thread Alban Gruin
Hi Phillip,

Le 02/11/2018 à 11:09, Phillip Wood a écrit :
 +    struct todo_item *items = NULL,
 +    base_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};
 +
 +    strbuf_addstr(buf, commands);
 +    base_item.offset_in_buf = buf->len - commands_len - 1;
 +    base_item.arg = buf->buf + base_item.offset_in_buf;
>>>
>>> I think if the user gives --exec more than once on the command line then
>>> commands will contain more than one exec command so this needs to parse
>>> commands and create one todo_item for each command.
>>>
>>
>> Ouch, you’re right.  Thanks for the heads up.
> 
> I haven't looked how difficult it would be but it might be best to
> change the option parsing to pass an array of strings containing the
> exec commands rather than one long string so we can just loop over the
> array here.
> 

It would be the best way to do so.  This string comes from git-rebase.sh
(or builtin/rebase.c) -- they format it this way before invoking
git-rebase--interactive.  So either I modify both of them (for this, I
would need to rebase my branch on master), or I can split this string in
builtin/rebase--interactive.c.  I prefer the first option, but maybe
changing the base of this series will not please Junio.

Cheers,
Alban



Re: [PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list

2018-11-01 Thread Alban Gruin
Le 30/10/2018 à 17:47, Phillip Wood a écrit :
> On 27/10/2018 22:29, Alban Gruin wrote:
>> This refactors sequencer_add_exec_commands() to work on a todo_list to
>> avoid redundant reads and writes to the disk.
>>
>> An obvious way to do this would be to insert the `exec' command between
>> the other commands, and reparse it once this is done.  This is not what
>> is done here.  Instead, the command is appended to the buffer once, and
>> a new list of items is created.  Items from the old list are copied to
>> it, and new `exec' items are appended when necessary.  This eliminates
>> the need to reparse the todo list, but this also means its buffer cannot
>> be directly written to the disk, hence todo_list_write_to_disk().
> 
> I'd reword this slightly, maybe
> 
> Instead of just inserting the `exec' command between the other commands,
> and re-parsing the buffer at the end the exec command is appended to the
> buffer once, and a new list of items is created.  Items from the old
> list are copied across and new `exec' items are appended when necessary.
>  This eliminates the need to reparse the buffer, but this also means we
> have to use todo_list_write_to_disk() to write the file.
> 
>> sequencer_add_exec_commands() still reads the todo list from the disk,
>> as it is needed by rebase -p.  todo_list_add_exec_commands() works on a
>> todo_list structure, and reparses it at the end.
> 
> I think the saying 'reparses' is confusing as that is what we're trying
> to avoid.
> 
>> complete_action() still uses sequencer_add_exec_commands() for now.
>> This will be changed in a future commit.
>>
>> Signed-off-by: Alban Gruin 
>> ---
>>   sequencer.c | 69 +
>>   1 file changed, 49 insertions(+), 20 deletions(-)
>>
>> diff --git a/sequencer.c b/sequencer.c
>> index e12860c047..12a3efeca8 100644
>> --- a/sequencer.c
>> +++ b/sequencer.c
>> @@ -4216,6 +4216,50 @@ int sequencer_make_script(FILE *out, int argc,
>> const char **argv,
>>   return 0;
>>   }
>>   +static void todo_list_add_exec_commands(struct todo_list *todo_list,
>> +    const char *commands)
>> +{
>> +    struct strbuf *buf = _list->buf;
>> +    const char *old_buf = buf->buf;
>> +    size_t commands_len = strlen(commands + strlen("exec ")) - 1;
>> +    int i, first = 1, nr = 0, alloc = 0;
> 
> Minor nit pick, I think it is clearer if first is initialized just
> before the loop as it is in the deleted code below.
> 
>> +    struct todo_item *items = NULL,
>> +    base_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};
>> +
>> +    strbuf_addstr(buf, commands);
>> +    base_item.offset_in_buf = buf->len - commands_len - 1;
>> +    base_item.arg = buf->buf + base_item.offset_in_buf;
> 
> I think if the user gives --exec more than once on the command line then
> commands will contain more than one exec command so this needs to parse
> commands and create one todo_item for each command.
> 

Ouch, you’re right.  Thanks for the heads up.

>> +
>> +    /*
>> + * Insert  after every pick. Here, fixup/squash chains
>> + * are considered part of the pick, so we insert the commands
>> *after*
>> + * those chains if there are any.
>> + */
>> +    for (i = 0; i < todo_list->nr; i++) {
>> +    enum todo_command command = todo_list->items[i].command;
>> +    if (todo_list->items[i].arg)
>> +    todo_list->items[i].arg = todo_list->items[i].arg -
>> old_buf + buf->buf;
>> +
>> +    if (command == TODO_PICK && !first) {
>> +    ALLOC_GROW(items, nr + 1, alloc);
>> +    memcpy(items + nr++, _item, sizeof(struct todo_item));
> 
> I think it would be clearer to say
> items[nr++] = base_item;
> rather than using memcpy. This applies below and to some of the other
> patches as well. Also this needs to loop over all the base_items if the
> user gave --exec more than once on the command line.
> 

I agree with you, it’s way more readable, IMO.  But for some reason, I
thought it was not possible to assign a struct to another in C.

> Best Wishes
> 
> Phillip
> 

Cheers,
Alban



Re: [PATCH v2 04/16] sequencer: introduce todo_list_write_to_file()

2018-11-01 Thread Alban Gruin
Hi Phillip,

Le 30/10/2018 à 17:28, Phillip Wood a écrit :
> Hi Alban
> 
> I like the direction this is going, it is an improvement on re-scanning
> the list at the end of each function.
> 
> On 27/10/2018 22:29, Alban Gruin wrote:
>> This introduce a new function to recreate the text of a todo list from
>> its commands, and then to write it to the disk.  This will be useful in
>> the future, the buffer of a todo list won’t be treated as a strict
>> mirror of the todo file by some of its functions once they will be
>> refactored.
> 
> I'd suggest rewording this slightly, maybe something like
> 
> This introduces a new function to recreate the text of a todo list from
> its commands and write it to a file. This will be useful as the next few
> commits will change the use of the buffer in struct todo_list so it will
> no-longer be a mirror of the file on disk.
> 
>> This functionnality can already be found in todo_list_transform(), but
> 
> s/functionnality/functionality/
> 
>> it is specifically made to replace the buffer of a todo list, which is
>> not the desired behaviour.  Thus, the part of todo_list_transform() that
>> actually creates the buffer is moved to a new function,
>> todo_list_to_strbuf().  The rest is unused, and so is dropped.
>>
>> todo_list_write_to_file() can also take care to append the help text to
> 
> s/care to append/care of appending/
> 
>> the buffer before writing it to the disk, or to write only the first n
>> items of the list.
> 
> Why/when do we only want to write a subset of the items?
>

In skip_unnecessary_picks(), in patch [10/16].  It needs to write the
elements of the todo list that were already done in the `done' file.

> […]
>> +int todo_list_write_to_file(struct todo_list *todo_list, const char
>> *file,
>> +    const char *shortrevisions, const char *shortonto,
>> +    int command_count, int append_help, int num, unsigned
>> flags)
> 
> This is a really long argument list which makes it easy for callers to
> get the parameters in the wrong order. I think append_help could
> probably be folded into the flags, I'm not sure what the command_count
> is used for but I've only read the first few patches. Maybe it would be
> better to pass a struct so we have named fields.
> 

You’re right, command_count is not really needed since we pass the
complete todo list.

The only bit that irks me is that, if I stop passing command_count, I
would have to call count_commands() twice in complete_action(): once to
check if there are any commands in the todo list, and again inside of
todo_list_write_to_file() (see [09/16].)

Perhaps I could move this check before calling todo_list_rearrange_squash()?

As a sidenote, this is not why I added command_count to the parameters
of todo_list_write_to_file().  It was a confusion of my part.

> Best Wishes
> 
> Phillip
> 

Cheers,
Alban



Re: [PATCH v2 00/16] sequencer: refactor functions working on a todo_list

2018-10-29 Thread Alban Gruin
Hi Junio,

Le 29/10/2018 à 04:05, Junio C Hamano a écrit :
> Alban Gruin  writes:
[…]
>> It is based onto ag/rebase-i-in-c (34b4731, "rebase -i: move
>> rebase--helper modes to rebase--interactive").
> 
> As there are quite a lot of fixes to the sequencer machinery since
> that topic forked from the mainline.  For example, [06/16] has
> unpleasant merge conflicts with 1ace63bc ("rebase --exec: make it
> work with --rebase-merges", 2018-08-09) that has been in master for
> the past couple of months.  IOW, the tip of ag/rebase-i-in-c is a
> bit too old a base to work on by now.  
> 
> I think I queued the previous round on the result of merging
> ag/rebase-i-in-c into master, i.e. 61dc7b24 ("Merge branch
> 'ag/rebase-i-in-c' into ag/sequencer-reduce-rewriting-todo",
> 2018-10-09).  That may be a more reasonable place to start this
> update on.
> 

Right.

My next iteration will be based on 61dc7b24, I just rebased my branch
onto it.



[PATCH v2 04/16] sequencer: introduce todo_list_write_to_file()

2018-10-27 Thread Alban Gruin
This introduce a new function to recreate the text of a todo list from
its commands, and then to write it to the disk.  This will be useful in
the future, the buffer of a todo list won’t be treated as a strict
mirror of the todo file by some of its functions once they will be
refactored.

This functionnality can already be found in todo_list_transform(), but
it is specifically made to replace the buffer of a todo list, which is
not the desired behaviour.  Thus, the part of todo_list_transform() that
actually creates the buffer is moved to a new function,
todo_list_to_strbuf().  The rest is unused, and so is dropped.

todo_list_write_to_file() can also take care to append the help text to
the buffer before writing it to the disk, or to write only the first n
items of the list.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 59 -
 sequencer.h |  4 +++-
 2 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 07296f1f57..0dd45677b1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4256,24 +4256,27 @@ int sequencer_add_exec_commands(const char *commands)
return i;
 }
 
-void todo_list_transform(struct todo_list *todo_list, unsigned flags)
+static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf 
*buf,
+   int num, unsigned flags)
 {
-   struct strbuf buf = STRBUF_INIT;
struct todo_item *item;
-   int i;
+   int i, max = todo_list->nr;
 
-   for (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {
+   if (num > 0 && num < max)
+   max = num;
+
+   for (item = todo_list->items, i = 0; i < max; i++, item++) {
/* if the item is not a command write it and continue */
if (item->command >= TODO_COMMENT) {
-   strbuf_addf(, "%.*s\n", item->arg_len, item->arg);
+   strbuf_addf(buf, "%.*s\n", item->arg_len, item->arg);
continue;
}
 
/* add command to the buffer */
if (flags & TODO_LIST_ABBREVIATE_CMDS)
-   strbuf_addch(, command_to_char(item->command));
+   strbuf_addch(buf, command_to_char(item->command));
else
-   strbuf_addstr(, command_to_string(item->command));
+   strbuf_addstr(buf, command_to_string(item->command));
 
/* add commit id */
if (item->commit) {
@@ -4283,27 +4286,46 @@ void todo_list_transform(struct todo_list *todo_list, 
unsigned flags)
 
if (item->command == TODO_MERGE) {
if (item->flags & TODO_EDIT_MERGE_MSG)
-   strbuf_addstr(, " -c");
+   strbuf_addstr(buf, " -c");
else
-   strbuf_addstr(, " -C");
+   strbuf_addstr(buf, " -C");
}
 
-   strbuf_addf(, " %s", oid);
+   strbuf_addf(buf, " %s", oid);
}
 
/* add all the rest */
if (!item->arg_len)
-   strbuf_addch(, '\n');
+   strbuf_addch(buf, '\n');
else
-   strbuf_addf(, " %.*s\n", item->arg_len, item->arg);
+   strbuf_addf(buf, " %.*s\n", item->arg_len, item->arg);
}
+}
 
-   strbuf_reset(_list->buf);
-   strbuf_add(_list->buf, buf.buf, buf.len);
+int todo_list_write_to_file(struct todo_list *todo_list, const char *file,
+   const char *shortrevisions, const char *shortonto,
+   int command_count, int append_help, int num, 
unsigned flags)
+{
+   int edit_todo = !(shortrevisions && shortonto), res;
+   struct strbuf buf = STRBUF_INIT;
+
+   todo_list_to_strbuf(todo_list, , num, flags);
+
+   if (append_help) {
+   if (!edit_todo) {
+   strbuf_addch(, '\n');
+   strbuf_commented_addf(, Q_("Rebase %s onto %s (%d 
command)",
+  "Rebase %s onto %s (%d 
commands)",
+  command_count),
+ shortrevisions, shortonto, 
command_count);
+   }
+   append_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, );
+   }
+
+   res = write_message(buf.buf, buf.len, file, 0);
strbuf_release();
 
-   if (todo_list_parse_insn_

[PATCH v2 09/16] sequencer: change complete_action() to use the refactored functions

2018-10-27 Thread Alban Gruin
complete_action() used functions that read the todo-list file, made some
changes to it, and wrote it back to the disk.

The previous commits were dedicated to separate the part that deals with
the file from the actual logic of these functions.  Now that this is
done, we can call directly the "logic" functions to avoid useless file
access.

The parsing of the list has to be done by the caller.  If the buffer of
the todo list provided by the caller is empty, a `noop' command is
directly added to the todo list, without touching to the buffer.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 16 ++-
 sequencer.c   | 82 ---
 sequencer.h   |  2 +-
 3 files changed, 42 insertions(+), 58 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index eef1ff2e83..c07330bdff 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
struct argv_array make_script_args = ARGV_ARRAY_INIT;
-   FILE *todo_list_file;
struct todo_list todo_list = TODO_LIST_INIT;
 
if (prepare_branch_to_be_rebased(opts, switch_to))
@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   todo_list_file = fopen(rebase_path_todo(), "w");
-   if (!todo_list_file) {
-   free(revisions);
-   free(shortrevisions);
-
-   return error_errno(_("could not open %s"), rebase_path_todo());
-   }
-
argv_array_pushl(_script_args, "", revisions, NULL);
if (restrict_revision)
argv_array_push(_script_args, restrict_revision);
@@ -109,15 +100,16 @@ static int do_interactive_rebase(struct replay_opts 
*opts, unsigned flags,
ret = sequencer_make_script(_list.buf,
make_script_args.argc, 
make_script_args.argv,
flags);
-   fputs(todo_list.buf.buf, todo_list_file);
-   fclose(todo_list_file);
 
if (ret)
error(_("could not generate todo list"));
else {
discard_cache();
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
+   BUG("unusable todo list");
+
ret = complete_action(opts, flags, shortrevisions, onto_name, 
onto,
- head_hash, cmd, autosquash);
+ head_hash, cmd, autosquash, _list);
}
 
free(revisions);
diff --git a/sequencer.c b/sequencer.c
index 94167588a2..1f7579328b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4501,93 +4501,85 @@ static int skip_unnecessary_picks(struct object_id 
*output_oid)
return 0;
 }
 
+static int todo_list_rearrange_squash(struct todo_list *todo_list);
+
 int complete_action(struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
const char *onto, const char *orig_head, const char *cmd,
-   unsigned autosquash)
+   unsigned autosquash, struct todo_list *todo_list)
 {
const char *shortonto, *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   struct strbuf *buf = &(todo_list.buf);
+   struct todo_list new_todo = TODO_LIST_INIT;
+   struct strbuf *buf = _list->buf;
struct object_id oid;
-   struct stat st;
+   int command_count;
 
get_oid(onto, );
shortonto = find_unique_abbrev(, DEFAULT_ABBREV);
 
-   if (!lstat(todo_file, ) && st.st_size == 0 &&
-   write_message("noop\n", 5, todo_file, 0))
-   return -1;
+   if (buf->len == 0) {
+   struct todo_item *item = append_new_todo(todo_list);
+   item->command = TODO_NOOP;
+   item->commit = NULL;
+   item->arg = NULL;
+   item->arg_len = item->flags = item->offset_in_buf = 0;
+   }
 
-   if (autosquash && rearrange_squash_in_todo_file())
+   if (autosquash && todo_list_rearrange_squash(todo_list))
return -1;
 
if (cmd && *cmd)
-   sequencer_add_exec_commands(cmd);
-
-   if (strbuf_read_file(buf, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(buf->buf, _list)) {
-   todo_list_release(_list);
-   return error(_("

[PATCH v2 14/16] sequencer: use edit_todo_list() in complete_action()

2018-10-27 Thread Alban Gruin
This changes complete_action() to use edit_todo_list(), now that it can
handle the initial edit of the todo list.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 22 +++---
 1 file changed, 7 insertions(+), 15 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index a03505f582..0763eeae25 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4457,7 +4457,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
struct todo_list new_todo = TODO_LIST_INIT;
struct strbuf *buf = _list->buf;
struct object_id oid;
-   int command_count;
+   int command_count, res;
 
get_oid(onto, );
shortonto = find_unique_abbrev(, DEFAULT_ABBREV);
@@ -4484,24 +4484,16 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
return error(_("nothing to do"));
}
 
-   if (todo_list_write_to_file(todo_list, todo_file,
-   shortrevisions, shortonto, command_count,
-   1, -1, flags | TODO_LIST_SHORTEN_IDS))
-   return error_errno(_("could not write '%s'"), todo_file);
-
-   if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
-   return error(_("could not copy '%s' to '%s'."), todo_file,
-rebase_path_todo_backup());
-
-   if (launch_sequence_editor(todo_file, _todo.buf, NULL)) {
+   res = edit_todo_list(todo_list, _todo, flags,
+command_count, shortrevisions, shortonto);
+   if (res == -1)
+   return -1;
+   else if (res == -2) {
apply_autostash(opts);
sequencer_remove_state(opts);
 
return -1;
-   }
-
-   strbuf_stripspace(_todo.buf, 1);
-   if (new_todo.buf.len == 0) {
+   } else if (res == -3) {
apply_autostash(opts);
sequencer_remove_state(opts);
todo_list_release(_todo);
-- 
2.19.1



[PATCH v2 13/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit

2018-10-27 Thread Alban Gruin
edit_todo_list() is changed to work on a todo_list, and to handle the
initial edition of the todo list (ie. making a backup of the todo
list).

It does not check for dropped commits yet, as todo_list_check() does not
take the commits that have already been processed by the rebase (ie. the
todo list is edited in the middle of a rebase session).

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 23 -
 rebase-interactive.c  | 48 ++-
 rebase-interactive.h  |  4 ++-
 sequencer.c   |  3 +--
 sequencer.h   |  1 +
 5 files changed, 52 insertions(+), 27 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index c07330bdff..0f83422aa0 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -13,6 +13,27 @@ static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 
+static int edit_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT,
+   new_todo = TODO_LIST_INIT;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   strbuf_stripspace(_list.buf, 1);
+   if (!edit_todo_list(_list, _todo, flags, 0, NULL, NULL) &&
+   todo_list_write_to_file(_todo, todo_file, NULL, NULL, 0, 0, -1,
+   flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)
+   return error_errno(_("could not write '%s'"), todo_file);
+
+   todo_list_release(_list);
+   todo_list_release(_todo);
+
+   return 0;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
   char **revisions, char **shortrevisions)
@@ -234,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
break;
}
case EDIT_TODO:
-   ret = edit_todo_list(flags);
+   ret = edit_todo_file(flags);
break;
case SHOW_CURRENT_PATCH: {
struct child_process cmd = CHILD_PROCESS_INIT;
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 0643d54b1b..d7a0818ac9 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -86,35 +86,37 @@ void append_todo_help(unsigned keep_empty, int 
command_count,
}
 }
 
-int edit_todo_list(unsigned flags)
+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,
+  unsigned flags, int command_count,
+  const char *shortrevisions, const char *shortonto)
 {
const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   int res = 0;
-
-   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   strbuf_stripspace(_list.buf, 1);
-   todo_list_parse_insn_buffer(todo_list.buf.buf, _list);
-   if (todo_list_write_to_file(_list, todo_file, NULL, NULL, 0, 1, -1,
-   flags | TODO_LIST_SHORTEN_IDS)) {
-   todo_list_release(_list);
-   return -1;
+   unsigned initial = shortrevisions && shortonto;
+
+   if (initial) {
+   todo_list_write_to_file(todo_list, todo_file, shortrevisions, 
shortonto,
+   0, 1, -1, flags | 
TODO_LIST_SHORTEN_IDS);
+
+   if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
+   return error(_("could not copy '%s' to '%s'."), 
todo_file,
+rebase_path_todo_backup());
+   } else {
+   todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);
+   todo_list_write_to_file(todo_list, todo_file, NULL, NULL, 0, 1, 
-1,
+   flags | TODO_LIST_SHORTEN_IDS);
}
 
-   strbuf_reset(_list.buf);
-   if (launch_sequence_editor(todo_file, _list.buf, NULL)) {
-   todo_list_release(_list);
-   return -1;
-   }
+   if (launch_sequence_editor(todo_file, _todo->buf, NULL))
+   return -2;
 
-   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
-   res = todo_list_write_to_file(_list, todo_file, NULL, 
NULL, 0, 0, -1,
- flags & ~(TODO_LIST_SHORTEN_IDS));
+   strbuf_stripspace(_todo->buf, 1);
+   if (initial && new_todo->buf.len == 0)
+   return -3;
 
-   todo_list_release(_list);
-   ret

[PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list

2018-10-27 Thread Alban Gruin
This refactors sequencer_add_exec_commands() to work on a todo_list to
avoid redundant reads and writes to the disk.

An obvious way to do this would be to insert the `exec' command between
the other commands, and reparse it once this is done.  This is not what
is done here.  Instead, the command is appended to the buffer once, and
a new list of items is created.  Items from the old list are copied to
it, and new `exec' items are appended when necessary.  This eliminates
the need to reparse the todo list, but this also means its buffer cannot
be directly written to the disk, hence todo_list_write_to_disk().

sequencer_add_exec_commands() still reads the todo list from the disk,
as it is needed by rebase -p.  todo_list_add_exec_commands() works on a
todo_list structure, and reparses it at the end.

complete_action() still uses sequencer_add_exec_commands() for now.
This will be changed in a future commit.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 69 +
 1 file changed, 49 insertions(+), 20 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index e12860c047..12a3efeca8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4216,6 +4216,50 @@ int sequencer_make_script(FILE *out, int argc, const 
char **argv,
return 0;
 }
 
+static void todo_list_add_exec_commands(struct todo_list *todo_list,
+   const char *commands)
+{
+   struct strbuf *buf = _list->buf;
+   const char *old_buf = buf->buf;
+   size_t commands_len = strlen(commands + strlen("exec ")) - 1;
+   int i, first = 1, nr = 0, alloc = 0;
+   struct todo_item *items = NULL,
+   base_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};
+
+   strbuf_addstr(buf, commands);
+   base_item.offset_in_buf = buf->len - commands_len - 1;
+   base_item.arg = buf->buf + base_item.offset_in_buf;
+
+   /*
+* Insert  after every pick. Here, fixup/squash chains
+* are considered part of the pick, so we insert the commands *after*
+* those chains if there are any.
+*/
+   for (i = 0; i < todo_list->nr; i++) {
+   enum todo_command command = todo_list->items[i].command;
+   if (todo_list->items[i].arg)
+   todo_list->items[i].arg = todo_list->items[i].arg - 
old_buf + buf->buf;
+
+   if (command == TODO_PICK && !first) {
+   ALLOC_GROW(items, nr + 1, alloc);
+   memcpy(items + nr++, _item, sizeof(struct 
todo_item));
+   }
+
+   ALLOC_GROW(items, nr + 1, alloc);
+   memcpy(items + nr++, todo_list->items + i, sizeof(struct 
todo_item));
+   first = 0;
+   }
+
+   /* insert or append final  */
+   ALLOC_GROW(items, nr + 1, alloc);
+   memcpy(items + nr++, _item, sizeof(struct todo_item));
+
+   FREE_AND_NULL(todo_list->items);
+   todo_list->items = items;
+   todo_list->nr = nr;
+   todo_list->alloc = alloc;
+}
+
 /*
  * Add commands after pick and (series of) squash/fixup commands
  * in the todo list.
@@ -4224,10 +4268,7 @@ int sequencer_add_exec_commands(const char *commands)
 {
const char *todo_file = rebase_path_todo();
struct todo_list todo_list = TODO_LIST_INIT;
-   struct todo_item *item;
-   struct strbuf *buf = _list.buf;
-   size_t offset = 0, commands_len = strlen(commands);
-   int i, first;
+   int res;
 
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error(_("could not read '%s'."), todo_file);
@@ -4237,23 +4278,11 @@ int sequencer_add_exec_commands(const char *commands)
return error(_("unusable todo list: '%s'"), todo_file);
}
 
-   first = 1;
-   /* insert  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  */
-   strbuf_add(buf, commands, commands_len);
-
-   i = write_message(buf->buf, buf->len, todo_file, 0);
+   todo_list_add_exec_commands(_list, commands);
+   res = todo_list_write_to_file(_list, todo_file, NULL, NULL, 0, 0, 
-1, 0);
todo_list_release(_list);
-   return i;
+
+   return res;
 }
 
 static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf 
*buf,
-- 
2.19.1



[PATCH v2 10/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list

2018-10-27 Thread Alban Gruin
This refactors skip_unnecessary_picks() to work on a todo_list.  The
file-handling logic is completely dropped here, as its only usage is
made by complete_action().

Instead of truncating the todo list’s buffer, the items are moved to
the beginning of the list, eliminating the need to reparse the list.
This also means its buffer cannot be directly written to the disk.

rewrite_file() is then removed, as it is now unused.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 80 -
 1 file changed, 18 insertions(+), 62 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 1f7579328b..7286498572 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4408,52 +4408,21 @@ int check_todo_list_from_file(void)
return res;
 }
 
-static int rewrite_file(const char *path, const char *buf, size_t len)
-{
-   int rc = 0;
-   int fd = open(path, O_WRONLY | O_TRUNC);
-   if (fd < 0)
-   return error_errno(_("could not open '%s' for writing"), path);
-   if (write_in_full(fd, buf, len) < 0)
-   rc = error_errno(_("could not write to '%s'"), path);
-   if (close(fd) && !rc)
-   rc = error_errno(_("could not close '%s'"), path);
-   return rc;
-}
-
 /* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct object_id *output_oid)
+static int skip_unnecessary_picks(struct todo_list *todo_list,
+ struct object_id *output_oid)
 {
-   const char *todo_file = rebase_path_todo();
-   struct strbuf buf = STRBUF_INIT;
-   struct todo_list todo_list = TODO_LIST_INIT;
struct object_id *parent_oid;
-   int fd, i;
-
-   if (!read_oneliner(, rebase_path_onto(), 0))
-   return error(_("could not read 'onto'"));
-   if (get_oid(buf.buf, output_oid)) {
-   strbuf_release();
-   return error(_("need a HEAD to fixup"));
-   }
-   strbuf_release();
-
-   if (strbuf_read_file_or_whine(_list.buf, todo_file) < 0)
-   return -1;
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
+   int i;
 
-   for (i = 0; i < todo_list.nr; i++) {
-   struct todo_item *item = todo_list.items + i;
+   for (i = 0; i < todo_list->nr; i++) {
+   struct todo_item *item = todo_list->items + i;
 
if (item->command >= TODO_NOOP)
continue;
if (item->command != TODO_PICK)
break;
if (parse_commit(item->commit)) {
-   todo_list_release(_list);
return error(_("could not parse commit '%s'"),
oid_to_hex(>commit->object.oid));
}
@@ -4467,37 +4436,22 @@ static int skip_unnecessary_picks(struct object_id 
*output_oid)
oidcpy(output_oid, >commit->object.oid);
}
if (i > 0) {
-   int offset = get_item_line_offset(_list, i);
const char *done_path = rebase_path_done();
 
-   fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
-   if (fd < 0) {
-   error_errno(_("could not open '%s' for writing"),
-   done_path);
-   todo_list_release(_list);
-   return -1;
-   }
-   if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+   if (todo_list_write_to_file(todo_list, done_path, NULL, NULL, 
0, 0, i, 0)) {
error_errno(_("could not write to '%s'"), done_path);
-   todo_list_release(_list);
-   close(fd);
return -1;
}
-   close(fd);
 
-   if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
-todo_list.buf.len - offset) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
+   memmove(todo_list->items, todo_list->items + i,
+   sizeof(struct todo_item) * (todo_list->nr - i));
+   todo_list->nr -= i;
+   todo_list->current = 0;
 
-   todo_list.current = i;
-   if (is_fixup(peek_command(_list, 0)))
-   record_in_rewritten(output_oid, 
peek_command(_list, 0));
+   if (is_fixup(peek_command(todo_list, 0)))
+   record_in_rewritten(output_oid, peek_command(todo_list, 
0));
}
 
-   todo_list_release(_list);
-
return 0;
 }
 
@@ -4573,6 +4527,11 

[PATCH v2 05/16] sequencer: refactor check_todo_list() to work on a todo_list

2018-10-27 Thread Alban Gruin
This refactors check_todo_list() to work on a todo_list to avoid
redundant reads and writes to the disk.  The function is renamed
todo_list_check().  The parsing of the two todo lists is left to the
caller.

As rebase -p still need to check the todo list from the disk, a new
function is introduced, check_todo_list_from_file().  It reads the file
from the disk, parses it, pass the todo_list to todo_list_check(), and
writes it back to the disk.

As get_missing_commit_check_level() and the enum
missing_commit_check_level are no longer needed inside of sequencer.c,
they are moved to rebase-interactive.c, and made static again.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |   2 +-
 rebase-interactive.c  |  90 -
 rebase-interactive.h  |   1 +
 sequencer.c   | 120 +++---
 sequencer.h   |   9 +--
 5 files changed, 115 insertions(+), 107 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index abdf6126df..c1d87c0fe6 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -255,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
ret = transform_todo_file(flags);
break;
case CHECK_TODO_LIST:
-   ret = check_todo_list();
+   ret = check_todo_list_from_file();
break;
case REARRANGE_SQUASH:
ret = rearrange_squash();
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 49f2f549e1..9e81ecfe9f 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -1,8 +1,32 @@
 #include "cache.h"
 #include "commit.h"
-#include "rebase-interactive.h"
 #include "sequencer.h"
+#include "rebase-interactive.h"
 #include "strbuf.h"
+#include "commit-slab.h"
+#include "config.h"
+
+enum missing_commit_check_level {
+   MISSING_COMMIT_CHECK_IGNORE = 0,
+   MISSING_COMMIT_CHECK_WARN,
+   MISSING_COMMIT_CHECK_ERROR
+};
+
+static enum missing_commit_check_level get_missing_commit_check_level(void)
+{
+   const char *value;
+
+   if (git_config_get_value("rebase.missingcommitscheck", ) ||
+   !strcasecmp("ignore", value))
+   return MISSING_COMMIT_CHECK_IGNORE;
+   if (!strcasecmp("warn", value))
+   return MISSING_COMMIT_CHECK_WARN;
+   if (!strcasecmp("error", value))
+   return MISSING_COMMIT_CHECK_ERROR;
+   warning(_("unrecognized setting %s for option "
+ "rebase.missingCommitsCheck. Ignoring."), value);
+   return MISSING_COMMIT_CHECK_IGNORE;
+}
 
 void append_todo_help(unsigned edit_todo, unsigned keep_empty,
  struct strbuf *buf)
@@ -88,3 +112,67 @@ int edit_todo_list(unsigned flags)
 
return 0;
 }
+
+define_commit_slab(commit_seen, unsigned char);
+/*
+ * Check if the user dropped some commits by mistake
+ * Behaviour determined by rebase.missingCommitsCheck.
+ * Check if there is an unrecognized command or a
+ * bad SHA-1 in a command.
+ */
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
+{
+   enum missing_commit_check_level check_level = 
get_missing_commit_check_level();
+   struct strbuf missing = STRBUF_INIT;
+   int res = 0, i;
+   struct commit_seen commit_seen;
+
+   init_commit_seen(_seen);
+
+   if (check_level == MISSING_COMMIT_CHECK_IGNORE)
+   goto leave_check;
+
+   /* Mark the commits in git-rebase-todo as seen */
+   for (i = 0; i < new_todo->nr; i++) {
+   struct commit *commit = new_todo->items[i].commit;
+   if (commit)
+   *commit_seen_at(_seen, commit) = 1;
+   }
+
+   /* Find commits in git-rebase-todo.backup yet unseen */
+   for (i = old_todo->nr - 1; i >= 0; i--) {
+   struct todo_item *item = old_todo->items + i;
+   struct commit *commit = item->commit;
+   if (commit && !*commit_seen_at(_seen, commit)) {
+   strbuf_addf(, " - %s %.*s\n",
+   find_unique_abbrev(>object.oid, 
DEFAULT_ABBREV),
+   item->arg_len, item->arg);
+   *commit_seen_at(_seen, commit) = 1;
+   }
+   }
+
+   /* Warn about missing commits */
+   if (!missing.len)
+   goto leave_check;
+
+   if (check_level == MISSING_COMMIT_CHECK_ERROR)
+   res = 1;
+
+   fprintf(stderr,
+   _("Warning: some commits may have been dropped accidentally.\n"
+   "Dropped commits (newer to older):\n"));
+
+   /* Make the list u

[PATCH v2 08/16] sequencer: make sequencer_make_script() write its script to a strbuf

2018-10-27 Thread Alban Gruin
This makes sequencer_make_script() write its script to a strbuf (ie. the
buffer of a todo_list) instead of a FILE.  This reduce the amount of
read/write made by rebase interactive.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 13 +++-
 sequencer.c   | 38 ---
 sequencer.h   |  2 +-
 3 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index f827e53f05..eef1ff2e83 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
struct argv_array make_script_args = ARGV_ARRAY_INIT;
-   FILE *todo_list;
+   FILE *todo_list_file;
+   struct todo_list todo_list = TODO_LIST_INIT;
 
if (prepare_branch_to_be_rebased(opts, switch_to))
return -1;
@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   todo_list = fopen(rebase_path_todo(), "w");
-   if (!todo_list) {
+   todo_list_file = fopen(rebase_path_todo(), "w");
+   if (!todo_list_file) {
free(revisions);
free(shortrevisions);
 
@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts 
*opts, unsigned flags,
if (restrict_revision)
argv_array_push(_script_args, restrict_revision);
 
-   ret = sequencer_make_script(todo_list,
+   ret = sequencer_make_script(_list.buf,
make_script_args.argc, 
make_script_args.argv,
flags);
-   fclose(todo_list);
+   fputs(todo_list.buf.buf, todo_list_file);
+   fclose(todo_list_file);
 
if (ret)
error(_("could not generate todo list"));
@@ -120,6 +122,7 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
 
free(revisions);
free(shortrevisions);
+   todo_list_release(_list);
argv_array_clear(_script_args);
 
return ret;
diff --git a/sequencer.c b/sequencer.c
index 09e32f3e5a..94167588a2 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3927,7 +3927,7 @@ static const char *label_oid(struct object_id *oid, const 
char *label,
 }
 
 static int make_script_with_merges(struct pretty_print_context *pp,
-  struct rev_info *revs, FILE *out,
+  struct rev_info *revs, struct strbuf *out,
   unsigned flags)
 {
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
@@ -4076,7 +4076,7 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
 * gathering commits not yet shown, reversing the list on the fly,
 * then outputting that list (labeling revisions as needed).
 */
-   fprintf(out, "%s onto\n", cmd_label);
+   strbuf_addf(out, "%s onto\n", cmd_label);
for (iter = tips; iter; iter = iter->next) {
struct commit_list *list = NULL, *iter2;
 
@@ -4086,9 +4086,9 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
entry = oidmap_get(, >object.oid);
 
if (entry)
-   fprintf(out, "\n# Branch %s\n", entry->string);
+   strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, 
entry->string);
else
-   fprintf(out, "\n");
+   strbuf_addch(out, '\n');
 
while (oidset_contains(, >object.oid) &&
   !oidset_contains(, >object.oid)) {
@@ -4101,8 +4101,8 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
}
 
if (!commit)
-   fprintf(out, "%s %s\n", cmd_reset,
-   rebase_cousins ? "onto" : "[new root]");
+   strbuf_addf(out, "%s %s\n", cmd_reset,
+   rebase_cousins ? "onto" : "[new root]");
else {
const char *to = NULL;
 
@@ -4115,12 +4115,12 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
   );
 
if (!to || !strcmp(to, "onto"))
-   fprintf(out, "%s onto\n", cmd_reset);
+   strbuf_addf(out, "%s onto\n", cmd_reset);
else {
   

[PATCH v2 12/16] rebase-interactive: append_todo_help() changes

2018-10-27 Thread Alban Gruin
This moves the writing of the comment "Rebase $shortrevisions onto
$shortonto ($command_count commands)" from complete_action() to
append_todo_help().

shortrevisions, shortonto, and command_count are passed as parameters to
append_todo_help().

During the initial edit of the todo list, shortrevisions and shortonto
are not NULL.  Therefore, if shortrevisions or shortonto is NULL, then
edit_todo would be true, otherwise it would be false.  Thus, edit_todo
is removed from the parameters of append_todo_help().

edit_todo_list() and complete_action() are modified to fit these
changes.

Signed-off-by: Alban Gruin 
---
 rebase-interactive.c | 12 +++-
 rebase-interactive.h |  3 ++-
 sequencer.c  | 16 
 3 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/rebase-interactive.c b/rebase-interactive.c
index b2be99a900..0643d54b1b 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -28,7 +28,8 @@ static enum missing_commit_check_level 
get_missing_commit_check_level(void)
return MISSING_COMMIT_CHECK_IGNORE;
 }
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
  struct strbuf *buf)
 {
const char *msg = _("\nCommands:\n"
@@ -47,6 +48,15 @@ void append_todo_help(unsigned edit_todo, unsigned 
keep_empty,
 ".   specified). Use -c  to reword the commit message.\n"
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
+   unsigned edit_todo = !(shortrevisions && shortonto);
+
+   if (!edit_todo) {
+   strbuf_addch(buf, '\n');
+   strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+ "Rebase %s onto %s (%d commands)",
+ command_count),
+ shortrevisions, shortonto, command_count);
+   }
 
strbuf_add_commented_lines(buf, msg, strlen(msg));
 
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 6bc7bc315d..61858f3a02 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -1,7 +1,8 @@
 #ifndef REBASE_INTERACTIVE_H
 #define REBASE_INTERACTIVE_H
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
  struct strbuf *buf);
 int edit_todo_list(unsigned flags);
 int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
diff --git a/sequencer.c b/sequencer.c
index 7286498572..4c664d6388 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4331,21 +4331,13 @@ int todo_list_write_to_file(struct todo_list 
*todo_list, const char *file,
const char *shortrevisions, const char *shortonto,
int command_count, int append_help, int num, 
unsigned flags)
 {
-   int edit_todo = !(shortrevisions && shortonto), res;
+   int res;
struct strbuf buf = STRBUF_INIT;
 
todo_list_to_strbuf(todo_list, , num, flags);
-
-   if (append_help) {
-   if (!edit_todo) {
-   strbuf_addch(, '\n');
-   strbuf_commented_addf(, Q_("Rebase %s onto %s (%d 
command)",
-  "Rebase %s onto %s (%d 
commands)",
-  command_count),
- shortrevisions, shortonto, 
command_count);
-   }
-   append_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, );
-   }
+   if (append_help)
+   append_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,
+shortrevisions, shortonto, );
 
res = write_message(buf.buf, buf.len, file, 0);
strbuf_release();
-- 
2.19.1



[PATCH v2 11/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()

2018-10-27 Thread Alban Gruin
Just like complete_action(), edit_todo_list() used a
function (transform_todo_file()) that read the todo list from the disk
and wrote it back, resulting in useless disk accesses.

This changes edit_todo_list() to call directly todo_list_write_to_file()
instead.

Signed-off-by: Alban Gruin 
---
 rebase-interactive.c | 38 --
 1 file changed, 16 insertions(+), 22 deletions(-)

diff --git a/rebase-interactive.c b/rebase-interactive.c
index 9e81ecfe9f..b2be99a900 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -78,39 +78,33 @@ void append_todo_help(unsigned edit_todo, unsigned 
keep_empty,
 
 int edit_todo_list(unsigned flags)
 {
-   struct strbuf buf = STRBUF_INIT;
const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res = 0;
 
-   if (strbuf_read_file(, todo_file, 0) < 0)
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
 
-   strbuf_stripspace(, 1);
-   if (write_message(buf.buf, buf.len, todo_file, 0)) {
-   strbuf_release();
+   strbuf_stripspace(_list.buf, 1);
+   todo_list_parse_insn_buffer(todo_list.buf.buf, _list);
+   if (todo_list_write_to_file(_list, todo_file, NULL, NULL, 0, 1, -1,
+   flags | TODO_LIST_SHORTEN_IDS)) {
+   todo_list_release(_list);
return -1;
}
 
-   strbuf_release();
-
-   transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);
-
-   if (strbuf_read_file(, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   append_todo_help(1, 0, );
-   if (write_message(buf.buf, buf.len, todo_file, 0)) {
-   strbuf_release();
+   strbuf_reset(_list.buf);
+   if (launch_sequence_editor(todo_file, _list.buf, NULL)) {
+   todo_list_release(_list);
return -1;
}
 
-   strbuf_release();
+   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
+   res = todo_list_write_to_file(_list, todo_file, NULL, 
NULL, 0, 0, -1,
+ flags & ~(TODO_LIST_SHORTEN_IDS));
 
-   if (launch_sequence_editor(todo_file, NULL, NULL))
-   return -1;
-
-   transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));
-
-   return 0;
+   todo_list_release(_list);
+   return res;
 }
 
 define_commit_slab(commit_seen, unsigned char);
-- 
2.19.1



[PATCH v2 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c

2018-10-27 Thread Alban Gruin
As transform_todo_file() is only needed inside of rebase--interactive.c,
it is moved there from sequencer.c.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 20 
 sequencer.c   | 20 
 sequencer.h   |  1 -
 3 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 0f83422aa0..28e6947ecb 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -34,6 +34,26 @@ static int edit_todo_file(unsigned flags)
return 0;
 }
 
+static int transform_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_release(_list);
+   return error(_("unusable todo list: '%s'"), todo_file);
+   }
+
+   res = todo_list_write_to_file(_list, todo_file,
+ NULL, NULL, 0, 0, -1, flags);
+   todo_list_release(_list);
+   return res;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
   char **revisions, char **shortrevisions)
diff --git a/sequencer.c b/sequencer.c
index 3069d4c6a2..a1b81bd544 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4344,26 +4344,6 @@ int todo_list_write_to_file(struct todo_list *todo_list, 
const char *file,
return res;
 }
 
-int transform_todo_file(unsigned flags)
-{
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   int res;
-
-   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
-   res = todo_list_write_to_file(_list, todo_file,
- NULL, NULL, 0, 0, -1, flags);
-   todo_list_release(_list);
-   return res;
-}
-
 static const char edit_todo_list_advice[] =
 N_("You can fix this with 'git rebase --edit-todo' "
 "and then run 'git rebase --continue'.\n"
diff --git a/sequencer.h b/sequencer.h
index 6a1a23541f..d35ca61d5d 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -134,7 +134,6 @@ int sequencer_make_script(struct strbuf *out, int argc, 
const char **argv,
  unsigned flags);
 
 int sequencer_add_exec_commands(const char *command);
-int transform_todo_file(unsigned flags);
 int check_todo_list_from_file(void);
 int complete_action(struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
-- 
2.19.1



[PATCH v2 15/16] sequencer: fix a call to error() in transform_todo_file()

2018-10-27 Thread Alban Gruin
This replaces a call to error() by a call to error_errno() after writing
the content of the todo list to the disk in transform_todo_file().

Signed-off-by: Alban Gruin 
---
No changes since v1, was commit 14/15.

 sequencer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sequencer.c b/sequencer.c
index 0763eeae25..3069d4c6a2 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4351,7 +4351,7 @@ int transform_todo_file(unsigned flags)
int res;
 
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
+   return error_errno(_("could not read '%s'."), todo_file);
 
if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
-- 
2.19.1



[PATCH v2 07/16] sequencer: refactor rearrange_squash() to work on a todo_list

2018-10-27 Thread Alban Gruin
This refactors rearrange_squash() to work on a todo_list to avoid
redundant reads and writes.  The function is renamed
todo_list_rearrange_squash().

The old version created a new buffer, which was directly written to the
disk.  This new version creates a new item list by just memcpying items
from the old item list, without creating a new buffer.  This eliminates
the need to reparse the todo list, but this also means its buffer cannot
be directly written to the disk.

As rebase -p still need to check the todo list from the disk, a new
function is introduced, rearrange_squash_in_todo_file().

complete_action() still uses rearrange_squash_in_todo_file() for now.
This will be changed in a future commit.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |  2 +-
 sequencer.c   | 87 +--
 sequencer.h   |  2 +-
 3 files changed, 45 insertions(+), 46 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index c1d87c0fe6..f827e53f05 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -258,7 +258,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
ret = check_todo_list_from_file();
break;
case REARRANGE_SQUASH:
-   ret = rearrange_squash();
+   ret = rearrange_squash_in_todo_file();
break;
case ADD_EXEC:
ret = sequencer_add_exec_commands(cmd);
diff --git a/sequencer.c b/sequencer.c
index 12a3efeca8..09e32f3e5a 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4523,7 +4523,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
write_message("noop\n", 5, todo_file, 0))
return -1;
 
-   if (autosquash && rearrange_squash())
+   if (autosquash && rearrange_squash_in_todo_file())
return -1;
 
if (cmd && *cmd)
@@ -4629,21 +4629,13 @@ define_commit_slab(commit_todo_item, struct todo_item 
*);
  * message will have to be retrieved from the commit (as the oneline in the
  * script cannot be trusted) in order to normalize the autosquash arrangement.
  */
-int rearrange_squash(void)
+static int todo_list_rearrange_squash(struct todo_list *todo_list)
 {
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
struct hashmap subject2item;
-   int res = 0, rearranged = 0, *next, *tail, i;
+   int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
char **subjects;
struct commit_todo_item commit_todo;
-
-   if (strbuf_read_file_or_whine(_list.buf, todo_file) < 0)
-   return -1;
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
+   struct todo_item *items = NULL;
 
init_commit_todo_item(_todo);
/*
@@ -4656,13 +4648,13 @@ int rearrange_squash(void)
 * be moved to appear after the i'th.
 */
hashmap_init(, (hashmap_cmp_fn) subject2item_cmp,
-NULL, todo_list.nr);
-   ALLOC_ARRAY(next, todo_list.nr);
-   ALLOC_ARRAY(tail, todo_list.nr);
-   ALLOC_ARRAY(subjects, todo_list.nr);
-   for (i = 0; i < todo_list.nr; i++) {
+NULL, todo_list->nr);
+   ALLOC_ARRAY(next, todo_list->nr);
+   ALLOC_ARRAY(tail, todo_list->nr);
+   ALLOC_ARRAY(subjects, todo_list->nr);
+   for (i = 0; i < todo_list->nr; i++) {
struct strbuf buf = STRBUF_INIT;
-   struct todo_item *item = todo_list.items + i;
+   struct todo_item *item = todo_list->items + i;
const char *commit_buffer, *subject, *p;
size_t subject_len;
int i2 = -1;
@@ -4675,7 +4667,6 @@ int rearrange_squash(void)
}
 
if (is_fixup(item->command)) {
-   todo_list_release(_list);
clear_commit_todo_item(_todo);
return error(_("the script was already rearranged."));
}
@@ -4710,7 +4701,7 @@ int rearrange_squash(void)
 *commit_todo_item_at(_todo, commit2))
/* found by commit name */
i2 = *commit_todo_item_at(_todo, commit2)
-   - todo_list.items;
+   - todo_list->items;
else {
/* copy can be a prefix of the commit subject */
for (i2 = 0; i2 < i; i2++)
@@ -4723,7 +4714,7 @@ int rearrange_squash(void)
}
if (i2 >= 0) {
rearranged = 1;
- 

[PATCH v2 01/16] sequencer: changes in parse_insn_buffer()

2018-10-27 Thread Alban Gruin
This clears the number of items of a todo_list before parsing it to
allow to parse the same list multiple times without issues.  As its
items are not dynamically allocated, or don’t need to allocate memory,
no additionnal memory management is required here.

Furthermore, if a line is invalid, the type of the corresponding
command is set to a garbage value, and its argument is defined properly.
This will allow to recreate the text of a todo list from its commands,
even if one of them is incorrect.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/sequencer.c b/sequencer.c
index 8dd6db5a01..9c8bd3f632 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2023,6 +2023,8 @@ static int parse_insn_buffer(char *buf, struct todo_list 
*todo_list)
char *p = buf, *next_p;
int i, res = 0, fixup_okay = file_exists(rebase_path_done());
 
+   todo_list->current = todo_list->nr = 0;
+
for (i = 1; *p; i++, p = next_p) {
char *eol = strchrnul(p, '\n');
 
@@ -2036,7 +2038,10 @@ static int parse_insn_buffer(char *buf, struct todo_list 
*todo_list)
if (parse_insn_line(item, p, eol)) {
res = error(_("invalid line %d: %.*s"),
i, (int)(eol - p), p);
-   item->command = TODO_NOOP;
+   item->command = TODO_COMMENT + 1;
+   item->arg = p;
+   item->arg_len = (int)(eol - p);
+   item->commit = NULL;
}
 
if (fixup_okay)
-- 
2.19.1



[PATCH v2 03/16] sequencer: refactor transform_todos() to work on a todo_list

2018-10-27 Thread Alban Gruin
This refactors transform_todos() to work on a todo_list.  The function
is renamed todo_list_transform().

As rebase -p still need to check the todo list from the disk, a new
function is introduced, transform_todo_file().  It is still used by
complete_action() and edit_todo_list() for now, but they will be
replaced in a future commit.

todo_list_transform() is not a static function, because it will be used
by edit_todo_list() from rebase-interactive.c in a future commit.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |  2 +-
 rebase-interactive.c  |  4 +--
 sequencer.c   | 46 +++
 sequencer.h   |  3 ++-
 4 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index a2ab68ed06..abdf6126df 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -252,7 +252,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
}
case SHORTEN_OIDS:
case EXPAND_OIDS:
-   ret = transform_todos(flags);
+   ret = transform_todo_file(flags);
break;
case CHECK_TODO_LIST:
ret = check_todo_list();
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 0f4119cbae..49f2f549e1 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -68,7 +68,7 @@ int edit_todo_list(unsigned flags)
 
strbuf_release();
 
-   transform_todos(flags | TODO_LIST_SHORTEN_IDS);
+   transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);
 
if (strbuf_read_file(, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
@@ -84,7 +84,7 @@ int edit_todo_list(unsigned flags)
if (launch_sequence_editor(todo_file, NULL, NULL))
return -1;
 
-   transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));
+   transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));
 
return 0;
 }
diff --git a/sequencer.c b/sequencer.c
index f791729271..07296f1f57 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4256,23 +4256,13 @@ int sequencer_add_exec_commands(const char *commands)
return i;
 }
 
-int transform_todos(unsigned flags)
+void todo_list_transform(struct todo_list *todo_list, 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(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
-   for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+   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(, "%.*s\n", item->arg_len, item->arg);
@@ -4308,9 +4298,33 @@ int transform_todos(unsigned flags)
strbuf_addf(, " %.*s\n", item->arg_len, item->arg);
}
 
-   i = write_message(buf.buf, buf.len, todo_file, 0);
+   strbuf_reset(_list->buf);
+   strbuf_add(_list->buf, buf.buf, buf.len);
+   strbuf_release();
+
+   if (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))
+   BUG("unusable todo list");
+}
+
+int transform_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error(_("could not read '%s'."), todo_file);
+
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_release(_list);
+   return error(_("unusable todo list: '%s'"), todo_file);
+   }
+
+   todo_list_transform(_list, flags);
+
+   res = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);
todo_list_release(_list);
-   return i;
+   return res;
 }
 
 enum missing_commit_check_level get_missing_commit_check_level(void)
@@ -4571,7 +4585,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
return error(_("could not copy '%s' to '%s'."), todo_file,
 rebase_path_todo_backup());
 
-   if (transform_todos(flags | TODO_LIST_SHORTEN_IDS))
+   if (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))
return error(_("could not transform the to

[PATCH v2 00/16] sequencer: refactor functions working on a todo_list

2018-10-27 Thread Alban Gruin
At the center of the "interactive" part of the interactive rebase lies
the todo list.  When the user starts an interactive rebase, a todo list
is generated, presented to the user (who then edits it using a text
editor), read back, and then is checked and processed before the actual
rebase takes place.

Some of this processing includes adding execs commands, reordering
fixup! and squash! commits, and checking if no commits were accidentally
dropped by the user.

Before I converted the interactive rebase in C, these functions were
called by git-rebase--interactive.sh through git-rebase--helper.  Since
the only way to pass around a large amount of data between a shell
script and a C program is to use a file (or any declination of a file),
the functions that checked and processed the todo list were directly
working on a file, the same file that the user edited.

During the conversion, I did not address this issue, which lead to a
complete_action() that reads the todo list file, does some computation
based on its content, and writes it back to the disk, several times in
the same function.

As it is not an efficient way to handle a data structure, this patch
series refactor the functions that processes the todo list to work on a
todo_list structure instead of reading it from the disk.

Some commits consists in modifying edit_todo_list() (initially used by
--edit-todo) to handle the initial edition of the todo list, to increase
code sharing.

It is based onto ag/rebase-i-in-c (34b4731, "rebase -i: move
rebase--helper modes to rebase--interactive").

Changes since v1:

 - When a line is invalid, parse_insn_buffer() sets the type of the
   corresponding command to a garbage value instead of `noop', and its
   argument is defined properly.

 - todo_list_add_exec_commands(), todo_list_rearrange_squash(),
   skip_unnecessary_picks() don’t reparse the todo list after processing
   them.  Instead, they recreate a new item list.

 - Due to the previous change, a todo list buffer can’t directly be
   written to the disk.  A new function, todo_list_write_to_disk(), is
   introduced to take care of this task.

 - rewrite_file() has been deleted.

 - A call to strbuf_addf(, "\n"); has been replaced by strbuf_addch(…).

 - complete_action() and todo_list_check() expect that their input todo list
   have already been parsed.

 - complete_action() no longer writes "noop\n" to the todo list buffer
   if it is empty.  Instead, it appends a `noop' command to the item
   list.

Alban Gruin (16):
  sequencer: changes in parse_insn_buffer()
  sequencer: make the todo_list structure public
  sequencer: refactor transform_todos() to work on a todo_list
  sequencer: introduce todo_list_write_to_file()
  sequencer: refactor check_todo_list() to work on a todo_list
  sequencer: refactor sequencer_add_exec_commands() to work on a
todo_list
  sequencer: refactor rearrange_squash() to work on a todo_list
  sequencer: make sequencer_make_script() write its script to a strbuf
  sequencer: change complete_action() to use the refactored functions
  sequencer: refactor skip_unnecessary_picks() to work on a todo_list
  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()
  rebase-interactive: append_todo_help() changes
  rebase-interactive: rewrite edit_todo_list() to handle the initial
edit
  sequencer: use edit_todo_list() in complete_action()
  sequencer: fix a call to error() in transform_todo_file()
  rebase--interactive: move transform_todo_file() to
rebase--interactive.c

 builtin/rebase--interactive.c |  68 +++-
 rebase-interactive.c  | 142 ++--
 rebase-interactive.h  |   8 +-
 sequencer.c   | 589 +-
 sequencer.h   |  68 +++-
 5 files changed, 455 insertions(+), 420 deletions(-)

-- 
2.19.1



[PATCH v2 02/16] sequencer: make the todo_list structure public

2018-10-27 Thread Alban Gruin
This makes the structures todo_list and todo_item, and the functions
todo_list_release() and parse_insn_buffer(), accessible outside of
sequencer.c.

Signed-off-by: Alban Gruin 
---
No changes since v1.

 sequencer.c | 66 +
 sequencer.h | 48 ++
 2 files changed, 59 insertions(+), 55 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 9c8bd3f632..f791729271 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1401,31 +1401,6 @@ static int allow_empty(struct replay_opts *opts, struct 
commit *commit)
return 1;
 }
 
-/*
- * Note that ordering matters in this enum. Not only must it match the mapping
- * below, it is also divided into several sections that matter.  When adding
- * new commands, make sure you add it in the right section.
- */
-enum todo_command {
-   /* commands that handle commits */
-   TODO_PICK = 0,
-   TODO_REVERT,
-   TODO_EDIT,
-   TODO_REWORD,
-   TODO_FIXUP,
-   TODO_SQUASH,
-   /* commands that do something else than handling a single commit */
-   TODO_EXEC,
-   TODO_LABEL,
-   TODO_RESET,
-   TODO_MERGE,
-   /* commands that do nothing but are counted for reporting progress */
-   TODO_NOOP,
-   TODO_DROP,
-   /* comments (not counted for reporting progress) */
-   TODO_COMMENT
-};
-
 static struct {
char c;
const char *str;
@@ -1897,26 +1872,7 @@ enum todo_item_flags {
TODO_EDIT_MERGE_MSG = 1
 };
 
-struct todo_item {
-   enum todo_command command;
-   struct commit *commit;
-   unsigned int flags;
-   const char *arg;
-   int arg_len;
-   size_t offset_in_buf;
-};
-
-struct todo_list {
-   struct strbuf buf;
-   struct todo_item *items;
-   int nr, alloc, current;
-   int done_nr, total_nr;
-   struct stat_data stat;
-};
-
-#define TODO_LIST_INIT { STRBUF_INIT }
-
-static void todo_list_release(struct todo_list *todo_list)
+void todo_list_release(struct todo_list *todo_list)
 {
strbuf_release(_list->buf);
FREE_AND_NULL(todo_list->items);
@@ -2017,7 +1973,7 @@ static int parse_insn_line(struct todo_item *item, const 
char *bol, char *eol)
return !item->commit;
 }
 
-static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list)
 {
struct todo_item *item;
char *p = buf, *next_p;
@@ -2115,7 +2071,7 @@ static int read_populate_todo(struct todo_list *todo_list,
return error(_("could not stat '%s'"), todo_file);
fill_stat_data(_list->stat, );
 
-   res = parse_insn_buffer(todo_list->buf.buf, todo_list);
+   res = todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
return error(_("please fix this using "
@@ -2146,7 +2102,7 @@ static int read_populate_todo(struct todo_list *todo_list,
FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
 
if (strbuf_read_file(, rebase_path_done(), 0) > 0 &&
-   !parse_insn_buffer(done.buf.buf, ))
+   !todo_list_parse_insn_buffer(done.buf.buf, 
))
todo_list->done_nr = count_commands();
else
todo_list->done_nr = 0;
@@ -4276,7 +4232,7 @@ int sequencer_add_exec_commands(const char *commands)
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error(_("could not read '%s'."), todo_file);
 
-   if (parse_insn_buffer(todo_list.buf.buf, _list)) {
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
return error(_("unusable todo list: '%s'"), todo_file);
}
@@ -4311,7 +4267,7 @@ int transform_todos(unsigned flags)
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error(_("could not read '%s'."), todo_file);
 
-   if (parse_insn_buffer(todo_list.buf.buf, _list)) {
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
return error(_("unusable todo list: '%s'"), todo_file);
}
@@ -4397,7 +4353,7 @@ int check_todo_list(void)
goto leave_check;
}
advise_to_edit_todo = res =
-   parse_insn_buffer(todo_list.buf.buf, _list);
+   todo_list_parse_insn_buffer(todo_list.buf.buf, _list);
 
if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
goto leave_check;
@@ -4416,7 +4372,7 @@ int check_todo_list(void)
goto leave_check;
}
strbuf_release(_file);
-   res = !!parse_insn_buffer(todo_list.buf.buf, _list);

Re: [PATCH v2 3/3] rebase (autostash): use an explicit OID to apply the stash

2018-10-23 Thread Alban Gruin
Hi Johannes,

this looks good to me, too!

Cheers,
Alban



Re: What's cooking in git.git (Oct 2018, #04; Fri, 19)

2018-10-20 Thread Alban Gruin
Le 19/10/2018 à 20:05, Alban Gruin a écrit :
> Le 19/10/2018 à 14:46, SZEDER Gábor a écrit :
>> On Fri, Oct 19, 2018 at 03:02:22PM +0900, Junio C Hamano wrote:
>>> Two large set of topics on "rebase in C" and "rebase -i in C" are
>>> now in 'next'.
>>
>> I see occasional failures in 't5520-pull.sh':
>>
>> […]
>>
>> When running t5520 in a loop, it tends to fail between 10-40
>> iterations, even when the machine is not under heavy load.
>>
>> It appears that these failures started with commit 5541bd5b8f (rebase:
>> default to using the builtin rebase, 2018-08-08), i.e. tip of
>> 'pk/rebase-in-c-6-final', but it's a "flip the big switch" commit, so
>> not very useful...
>>

The bug can be bisected by prepending the command chain of 5520.25 with
`git config --bool rebase.usebuiltin true &&`.  Commit 7debdaa4ad
(“builtin rebase: support `--autostash` option”) is the culprit, but
some commits that cause this test to fail with a different error (ie.
“TODO”…) must be marked as good.

The error comes from the call to `git stash apply $stash_id' in
builtin/rebase.c:261.  When $stash_id only contains decimals and no
letters, git-stash tries to apply stash@{$stash_id}[0][1].  Thas was not
a real problem with the shell script, because it did not abbreviate the
object id of the stashed commit, so it was very unlikely that the oid
would contain only digits.  builtin/rebase.c shortens the oid[2], making
this problem more likely to occur.

We could add a switch to git-stash to tell it that the parameter is an
oid, not anything else.

[0] https://github.com/git/git/blob/master/git-stash.sh#L514-L520
[1] https://github.com/git/git/blob/pu/builtin/stash.c#L167-L168
[2] https://github.com/git/git/blob/next/builtin/rebase.c#L1373

Cheers,
Alban



Re: What's cooking in git.git (Oct 2018, #04; Fri, 19)

2018-10-19 Thread Alban Gruin
Hi,

Le 19/10/2018 à 14:46, SZEDER Gábor a écrit :
> On Fri, Oct 19, 2018 at 03:02:22PM +0900, Junio C Hamano wrote:
>> Two large set of topics on "rebase in C" and "rebase -i in C" are
>> now in 'next'.
> 
> I see occasional failures in 't5520-pull.sh':
> 
> […]
>
> When running t5520 in a loop, it tends to fail between 10-40
> iterations, even when the machine is not under heavy load.
> 
> It appears that these failures started with commit 5541bd5b8f (rebase:
> default to using the builtin rebase, 2018-08-08), i.e. tip of
> 'pk/rebase-in-c-6-final', but it's a "flip the big switch" commit, so
> not very useful...
> 

I can reproduce this.

I also tried to run this specific test under valgrind, and found out
that some cases I did not targeted with --valgrind-only failed.  The
same thing happens with t3404, which did not crash with valgrind before.

Here is a log:

> expecting success: 
> HEAD=$(git rev-parse HEAD) &&
> set_fake_editor &&
> git rebase -i -p HEAD^ &&
> git update-index --refresh &&
> git diff-files --quiet &&
> git diff-index --quiet --cached HEAD -- &&
> test $HEAD = $(git rev-parse HEAD)
> 
> +++ git rev-parse HEAD
> ++ HEAD=d2d5ba71c6d0266f26238e804f77f026984ae0d9
> ++ set_fake_editor
> ++ write_script fake-editor.sh
> ++ echo '#!/bin/sh'
> ++ cat
> ++ chmod +x fake-editor.sh
> +++ pwd
> ++ test_set_editor '/tmp/git-alban/trash 
> directory.t3404-rebase-interactive/fake-editor.sh'
> ++ FAKE_EDITOR='/tmp/git-alban/trash 
> directory.t3404-rebase-interactive/fake-editor.sh'
> ++ export FAKE_EDITOR
> ++ EDITOR='"$FAKE_EDITOR"'
> ++ export EDITOR
> ++ git rebase -i -p 'HEAD^'
> GIT_DIR='/tmp/git-alban/trash directory.t3404-rebase-interactive/.git'; 
> state_dir='.git/rebase-merge'; upstream_name='HEAD^'; 
> upstream='8f99a4f1fbbd214b25a070ad34ec5a8f833522cc'; 
> head_name='refs/heads/branch1'; 
> orig_head='d2d5ba71c6d0266f26238e804f77f026984ae0d9'; 
> onto='8f99a4f1fbbd214b25a070ad34ec5a8f833522cc'; onto_name='HEAD^'; unset 
> revisions; unset restrict_revision; GIT_QUIET=''; git_am_opt=''; verbose=''; 
> diffstat=''; force_rebase=''; action=''; signoff=''; 
> allow_rerere_autoupdate=''; keep_empty=''; autosquash=''; unset gpg_sign_opt; 
> unset cmd; allow_empty_message='--allow-empty-message'; rebase_merges=''; 
> rebase_cousins=''; unset strategy; unset strategy_opts; rebase_root=''; 
> squash_onto=''; git_format_patch_opt=''; . git-sh-setup && . 
> git-rebase--common && . git-rebase--preserve-merges && 
> git_rebase__preserve_merges: line 0: .: git-rebase--common: file not found
> error: last command exited with $?=1
> not ok 25 - -p handles "no changes" gracefully
> #
> #   HEAD=$(git rev-parse HEAD) &&
> #   set_fake_editor &&
> #   git rebase -i -p HEAD^ &&
> #   git update-index --refresh &&
> #   git diff-files --quiet &&
> #   git diff-index --quiet --cached HEAD -- &&
> #   test $HEAD = $(git rev-parse HEAD)
> #

This comes from 't3404-rebase-interactive.sh', with --valgrind-only set
to '63'.

Cheers,
Alban



Re: [PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list

2018-10-12 Thread Alban Gruin
Le 12/10/2018 à 11:54, Phillip Wood a écrit :
> On 11/10/2018 17:57, Alban Gruin wrote:
> > Hi Phillip,
> > 
> > thanks for taking the time to review my patches.
> > 
> > Le 11/10/2018 à 13:25, Phillip Wood a écrit :
> >> On 07/10/2018 20:54, Alban Gruin wrote:
> >>> @@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char
> >>> *commands)
> >>>   }
> >>> /* insert or append final  */
> >>> -if (insert >= 0 && insert < todo_list.nr)
> >>> -strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
> >>> +if (insert >= 0 && insert < todo_list->nr)
> >>> +strbuf_insert(buf, todo_list->items[insert].offset_in_buf +
> >>> offset, commands, commands_len);
> >>>   else if (insert >= 0 || !offset)
> >>>   strbuf_add(buf, commands, commands_len);
> >>>   -i = write_message(buf->buf, buf->len, todo_file, 0);
> >>> +if (todo_list_parse_insn_buffer(buf->buf, todo_list))
> >>> +BUG("unusable todo list");}
> >> 
> >> It is a shame to have to re-parse the todo list, I wonder how difficult
> >> it would be to adjust the todo_list item array as the exec commands are
> >> inserted. The same applies to the next couple of patches
> > 
> > Good question.
> > 
> > This function inserts an `exec' command after every `pick' command.
> > These commands are stored in a dynamically allocated list, grew with
> > ALLOW_GROW().
> > 
> > If we want to keep the current structure, we would have to grow the size
> > of the list by 1 and move several element to the end every time we want
> > to add an `exec' command.  It would not be very effective.  Perhaps I
> > should use a linked list here, instead.  It may also work well with
> > rearrange_squash() and skip_unnecessary_picks().
> > 
> > Maybe we could even get rid of the strbuf at some point.
> 
> Another way would be to use the strbuf as a string pool rather than a
> copy of the text of the file. There could be a write_todo_list()
> function that takes a todo list and some flags, iterates over the items
> in the todo list and writes the file. The flags would specify whether to
> append the todo help and whether to abbreviate the object ids (so there
> is no need for a separate call to transform_todos()). Then
> add_exec_commands() could allocate a new array of todo items which it
> builds up as it works through the original list and replaces the
> original list with the new one at the end. The text of the exec items
> can be added to the end of the strbuf (we only need one copy of the exec
> text with this scheme). rearrange_squash() can use a temporary array to
> build a new list as well or just memmove() things but that might be
> slower if there is a lot of rearrangement to do. skip_unecessary_picks()
> could just set the current item to the one we want to start with.
> 

This sounds good, and it looks like the solution dscho proposed on IRC a few 
hours ago[0].  I will try to do this.

[0] http://colabti.org/irclogger/irclogger_log/git-devel?date=2018-10-12#l46

Cheers,
Alban






Re: [PATCH 10/15] rebase-interactive: use todo_list_transform() in edit_todo_list()

2018-10-11 Thread Alban Gruin
Le 11/10/2018 à 17:16, Phillip Wood a écrit :
> On 07/10/2018 20:54, Alban Gruin wrote:
>> Just like complete_action(), edit_todo_list() used a
>> function (transform_todo_file()) that read the todo-list from the disk
>> and wrote it back, resulting in useless disk accesses.
>>
>> This changes edit_todo_list() to call directly todo_list_transform()
>> instead.
>>
>> Signed-off-by: Alban Gruin 
>> ---
>>   rebase-interactive.c | 40 +++-
>>   1 file changed, 19 insertions(+), 21 deletions(-)
>>
>> diff --git a/rebase-interactive.c b/rebase-interactive.c
>> index 7c7f720a3d..f42d48e192 100644
>> --- a/rebase-interactive.c
>> +++ b/rebase-interactive.c
>> @@ -78,39 +78,37 @@ void append_todo_help(unsigned edit_todo, unsigned
>> keep_empty,
>>     int edit_todo_list(unsigned flags)
>>   {
>> -    struct strbuf buf = STRBUF_INIT;
>>   const char *todo_file = rebase_path_todo();
>> +    struct todo_list todo_list = TODO_LIST_INIT;
>> +    int res = 0;
>>   -    if (strbuf_read_file(, todo_file, 0) < 0)
>> +    if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
>>   return error_errno(_("could not read '%s'."), todo_file);
>>   -    strbuf_stripspace(, 1);
>> -    if (write_message(buf.buf, buf.len, todo_file, 0)) {
>> -    strbuf_release();
>> -    return -1;
>> -    }
>> -
>> -    strbuf_release();
>> +    strbuf_stripspace(_list.buf, 1);
>> +    if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
>> +    todo_list_transform(_list, flags | TODO_LIST_SHORTEN_IDS);
>>   -    transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);
>> -
>> -    if (strbuf_read_file(, todo_file, 0) < 0)
>> -    return error_errno(_("could not read '%s'."), todo_file);
>> +    append_todo_help(1, 0, _list.buf);
>>   -    append_todo_help(1, 0, );
> 
> I think this patch is fine, I was just wondering if you meant to move
> the call to append_todo_help() above the blank line?
> 

No

> Best Wishes
> 
> Phillip
> 

Cheers,
Alban



Re: [PATCH 08/15] sequencer: change complete_action() to use the refactored functions

2018-10-11 Thread Alban Gruin
Le 11/10/2018 à 15:51, Phillip Wood a écrit :
> On 07/10/2018 20:54, Alban Gruin wrote:
>> +    if (rewrite_file(todo_file, new_todo.buf.buf, new_todo.buf.len) <
>> 0) {
>> +    todo_list_release(_todo);
>> +    return error_errno(_("could not write '%s'"), todo_file);
>> +    }
> 
> rewrite_file() can truncate the old version of the file if there is an
> error when writing the new version, I think it would be better to use
> write_message() instead as that atomically updates the file. The same
> applies to patch 5 (refactor rearrange_squash()) after which I think
> there will be no callers to rewrite_file() so it can be deleted.

You’re right, I didn’t notice that.

> 
> Best Wishes
> 
> Phillip
> 

Cheers,
Alban



Re: [PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list

2018-10-11 Thread Alban Gruin
Hi Phillip,

thanks for taking the time to review my patches.

Le 11/10/2018 à 13:25, Phillip Wood a écrit :
> On 07/10/2018 20:54, Alban Gruin wrote:
>> @@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char
>> *commands)
>>   }
>>     /* insert or append final  */
>> -    if (insert >= 0 && insert < todo_list.nr)
>> -    strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
>> +    if (insert >= 0 && insert < todo_list->nr)
>> +    strbuf_insert(buf, todo_list->items[insert].offset_in_buf +
>>     offset, commands, commands_len);
>>   else if (insert >= 0 || !offset)
>>   strbuf_add(buf, commands, commands_len);
>>   -    i = write_message(buf->buf, buf->len, todo_file, 0);
>> +    if (todo_list_parse_insn_buffer(buf->buf, todo_list))
>> +    BUG("unusable todo list");}
> 
> It is a shame to have to re-parse the todo list, I wonder how difficult
> it would be to adjust the todo_list item array as the exec commands are
> inserted. The same applies to the next couple of patches
> 

Good question.

This function inserts an `exec' command after every `pick' command.
These commands are stored in a dynamically allocated list, grew with
ALLOW_GROW().

If we want to keep the current structure, we would have to grow the size
of the list by 1 and move several element to the end every time we want
to add an `exec' command.  It would not be very effective.  Perhaps I
should use a linked list here, instead.  It may also work well with
rearrange_squash() and skip_unnecessary_picks().

Maybe we could even get rid of the strbuf at some point.

> Best Wishes
> 
> Phillip
> 

Cheers,
Alban



Re: [PATCH 00/15] sequencer: refactor functions working on a todo_list

2018-10-07 Thread Alban Gruin
Le 07/10/2018 à 21:54, Alban Gruin a écrit :
> At the center of the "interactive" part of the interactive rebase lies
> the todo list.  When the user starts an interactive rebase, a todo list
> is generated, presented to the user (who then edits it using a text
> editor), read back, and then is checked and processed before the actual
> rebase takes place.
> 
> Some of this processing includes adding execs commands, reordering
> fixup! and squash! commits, and checking if no commits were accidentally
> dropped by the user.
> 
> Before I converted the interactive rebase in C, these functions were
> called by git-rebase--interactive.sh through git-rebase--helper.  Since
> the only way to pass around a large amount of data between a shell
> script and a C program is to use a file (or any declination of a file),
> the functions that checked and processed the todo list were directly
> working on a file, the same file that the user edited.
> 
> During the conversion, I did not address this issue, which lead to a
> complete_action() that reads the todo list file, does some computation
> based on its content, and writes it back to the disk, several times in
> the same function.
> 
> As it is not an efficient way to handle a data structure, this patch
> series refactor the functions that processes the todo list to work on a
> todo_list structure instead of reading it from the disk.
> 
> Some commits consists in modifying edit_todo_list() (initially used by
> --edit-todo) to handle the initial edition of the todo list, to increase
> code sharing.
> 

And it’s based on the 8th version of my patch series “rebase -i: rewrite
in C”.



[PATCH 07/15] sequencer: make sequencer_make_script() write its script to a strbuf

2018-10-07 Thread Alban Gruin
This makes sequencer_make_script() write its script to a strbuf (ie. the
buffer of a todo_list) instead of a FILE.  This reduce the amount of
read/write made by rebase interactive.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 13 +++-
 sequencer.c   | 38 ---
 sequencer.h   |  2 +-
 3 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index f827e53f05..eef1ff2e83 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
struct argv_array make_script_args = ARGV_ARRAY_INIT;
-   FILE *todo_list;
+   FILE *todo_list_file;
+   struct todo_list todo_list = TODO_LIST_INIT;
 
if (prepare_branch_to_be_rebased(opts, switch_to))
return -1;
@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   todo_list = fopen(rebase_path_todo(), "w");
-   if (!todo_list) {
+   todo_list_file = fopen(rebase_path_todo(), "w");
+   if (!todo_list_file) {
free(revisions);
free(shortrevisions);
 
@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts 
*opts, unsigned flags,
if (restrict_revision)
argv_array_push(_script_args, restrict_revision);
 
-   ret = sequencer_make_script(todo_list,
+   ret = sequencer_make_script(_list.buf,
make_script_args.argc, 
make_script_args.argv,
flags);
-   fclose(todo_list);
+   fputs(todo_list.buf.buf, todo_list_file);
+   fclose(todo_list_file);
 
if (ret)
error(_("could not generate todo list"));
@@ -120,6 +122,7 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
 
free(revisions);
free(shortrevisions);
+   todo_list_release(_list);
argv_array_clear(_script_args);
 
return ret;
diff --git a/sequencer.c b/sequencer.c
index 30a7fe3958..dfb8d1c974 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4083,7 +4083,7 @@ static const char *label_oid(struct object_id *oid, const 
char *label,
 }
 
 static int make_script_with_merges(struct pretty_print_context *pp,
-  struct rev_info *revs, FILE *out,
+  struct rev_info *revs, struct strbuf *out,
   unsigned flags)
 {
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
@@ -4230,7 +4230,7 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
 * gathering commits not yet shown, reversing the list on the fly,
 * then outputting that list (labeling revisions as needed).
 */
-   fprintf(out, "%s onto\n", cmd_label);
+   strbuf_addf(out, "%s onto\n", cmd_label);
for (iter = tips; iter; iter = iter->next) {
struct commit_list *list = NULL, *iter2;
 
@@ -4240,9 +4240,9 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
entry = oidmap_get(, >object.oid);
 
if (entry)
-   fprintf(out, "\n%c Branch %s\n", comment_line_char, 
entry->string);
+   strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, 
entry->string);
else
-   fprintf(out, "\n");
+   strbuf_addf(out, "\n");
 
while (oidset_contains(, >object.oid) &&
   !oidset_contains(, >object.oid)) {
@@ -4255,8 +4255,8 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
}
 
if (!commit)
-   fprintf(out, "%s %s\n", cmd_reset,
-   rebase_cousins ? "onto" : "[new root]");
+   strbuf_addf(out, "%s %s\n", cmd_reset,
+   rebase_cousins ? "onto" : "[new root]");
else {
const char *to = NULL;
 
@@ -4269,12 +4269,12 @@ static int make_script_with_merges(struct 
pretty_print_context *pp,
   );
 
if (!to || !strcmp(to, "onto"))
-   fprintf(out, "%s onto\n", cmd_reset);
+   strbuf_addf(out, "%s on

[PATCH 06/15] sequencer: refactor transform_todos() to work on a todo_list

2018-10-07 Thread Alban Gruin
This refactors transform_todos() to work on a todo_list.  The function
is renamed todo_list_transform().

As rebase -p still need to check the todo list from the disk, a new
function is introduced, transform_todo_file().

todo_list_transform() is not a static function, because it will be used
by edit_todo_list() from rebase-interactive.c in a future commit.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |  2 +-
 rebase-interactive.c  |  4 +--
 sequencer.c   | 46 +++
 sequencer.h   |  3 ++-
 4 files changed, 35 insertions(+), 20 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 8deef126d1..f827e53f05 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -252,7 +252,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
}
case SHORTEN_OIDS:
case EXPAND_OIDS:
-   ret = transform_todos(flags);
+   ret = transform_todo_file(flags);
break;
case CHECK_TODO_LIST:
ret = check_todo_list_from_file();
diff --git a/rebase-interactive.c b/rebase-interactive.c
index ef8540245d..7c7f720a3d 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -92,7 +92,7 @@ int edit_todo_list(unsigned flags)
 
strbuf_release();
 
-   transform_todos(flags | TODO_LIST_SHORTEN_IDS);
+   transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);
 
if (strbuf_read_file(, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
@@ -108,7 +108,7 @@ int edit_todo_list(unsigned flags)
if (launch_sequence_editor(todo_file, NULL, NULL))
return -1;
 
-   transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));
+   transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));
 
return 0;
 }
diff --git a/sequencer.c b/sequencer.c
index 8a6176b3d0..30a7fe3958 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4440,23 +4440,13 @@ int sequencer_add_exec_commands(const char *commands)
return res;
 }
 
-int transform_todos(unsigned flags)
+void todo_list_transform(struct todo_list *todo_list, 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(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
-   for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+   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(, "%.*s\n", item->arg_len, item->arg);
@@ -4492,9 +4482,33 @@ int transform_todos(unsigned flags)
strbuf_addf(, " %.*s\n", item->arg_len, item->arg);
}
 
-   i = write_message(buf.buf, buf.len, todo_file, 0);
+   strbuf_reset(_list->buf);
+   strbuf_add(_list->buf, buf.buf, buf.len);
+   strbuf_release();
+
+   if (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))
+   BUG("unusable todo list");
+}
+
+int transform_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error(_("could not read '%s'."), todo_file);
+
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_release(_list);
+   return error(_("unusable todo list: '%s'"), todo_file);
+   }
+
+   todo_list_transform(_list, flags);
+
+   res = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);
todo_list_release(_list);
-   return i;
+   return res;
 }
 
 int check_todo_list_from_file(void)
@@ -4670,7 +4684,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
return error(_("could not copy '%s' to '%s'."), todo_file,
 rebase_path_todo_backup());
 
-   if (transform_todos(flags | TODO_LIST_SHORTEN_IDS))
+   if (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))
return error(_("could not transform the todo list"));
 
strbuf_reset(buf);
@@ -4699,7 +4713,7 @@ int complete_action(struct replay_opts *opts, 

[PATCH 15/15] rebase--interactive: move transform_todo_file() to rebase--interactive.c

2018-10-07 Thread Alban Gruin
As transform_todo_file() is only needed inside of rebase--interactive.c,
it is moved there from sequencer.c.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 21 +
 sequencer.c   | 21 -
 sequencer.h   |  1 -
 3 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 264e940b47..50b5c25402 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -33,6 +33,27 @@ static int edit_todo_file(unsigned flags)
return 0;
 }
 
+static int transform_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_release(_list);
+   return error(_("unusable todo list: '%s'"), todo_file);
+   }
+
+   todo_list_transform(_list, flags);
+
+   res = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);
+   todo_list_release(_list);
+   return res;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
   char **revisions, char **shortrevisions)
diff --git a/sequencer.c b/sequencer.c
index 65bf251ba5..e837e52b64 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4485,27 +4485,6 @@ void todo_list_transform(struct todo_list *todo_list, 
unsigned flags)
BUG("unusable todo list");
 }
 
-int transform_todo_file(unsigned flags)
-{
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   int res;
-
-   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
-   todo_list_transform(_list, flags);
-
-   res = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);
-   todo_list_release(_list);
-   return res;
-}
-
 int check_todo_list_from_file(void)
 {
struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
diff --git a/sequencer.h b/sequencer.h
index fa84918c55..a4b0113206 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -137,7 +137,6 @@ int sequencer_make_script(struct strbuf *out, int argc, 
const char **argv,
  unsigned flags);
 
 int sequencer_add_exec_commands(const char *command);
-int transform_todo_file(unsigned flags);
 int check_todo_list_from_file(void);
 int complete_action(struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
-- 
2.19.1



[PATCH 08/15] sequencer: change complete_action() to use the refactored functions

2018-10-07 Thread Alban Gruin
complete_action() used functions that read the todo-list file, made some
changes to it, and wrote it back to the disk.

The previous commits were dedicated to separate the part that deals with
the file from the actual logic of these functions.  Now that this is
done, we can call directly the "logic" functions to avoid useless file
access.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 13 +-
 sequencer.c   | 76 +--
 sequencer.h   |  2 +-
 3 files changed, 38 insertions(+), 53 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index eef1ff2e83..0700339f90 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
const char *head_hash = NULL;
char *revisions = NULL, *shortrevisions = NULL;
struct argv_array make_script_args = ARGV_ARRAY_INIT;
-   FILE *todo_list_file;
struct todo_list todo_list = TODO_LIST_INIT;
 
if (prepare_branch_to_be_rebased(opts, switch_to))
@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, 
unsigned flags,
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   todo_list_file = fopen(rebase_path_todo(), "w");
-   if (!todo_list_file) {
-   free(revisions);
-   free(shortrevisions);
-
-   return error_errno(_("could not open %s"), rebase_path_todo());
-   }
-
argv_array_pushl(_script_args, "", revisions, NULL);
if (restrict_revision)
argv_array_push(_script_args, restrict_revision);
@@ -109,15 +100,13 @@ static int do_interactive_rebase(struct replay_opts 
*opts, unsigned flags,
ret = sequencer_make_script(_list.buf,
make_script_args.argc, 
make_script_args.argv,
flags);
-   fputs(todo_list.buf.buf, todo_list_file);
-   fclose(todo_list_file);
 
if (ret)
error(_("could not generate todo list"));
else {
discard_cache();
ret = complete_action(opts, flags, shortrevisions, onto_name, 
onto,
- head_hash, cmd, autosquash);
+ head_hash, cmd, autosquash, _list);
}
 
free(revisions);
diff --git a/sequencer.c b/sequencer.c
index dfb8d1c974..b37935e5ab 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4624,93 +4624,89 @@ static int skip_unnecessary_picks(struct object_id 
*output_oid)
return 0;
 }
 
+static int todo_list_rearrange_squash(struct todo_list *todo_list);
+
 int complete_action(struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
const char *onto, const char *orig_head, const char *cmd,
-   unsigned autosquash)
+   unsigned autosquash, struct todo_list *todo_list)
 {
const char *shortonto, *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   struct strbuf *buf = &(todo_list.buf);
+   struct todo_list new_todo = TODO_LIST_INIT;
+   struct strbuf *buf = _list->buf;
struct object_id oid;
-   struct stat st;
+   int command_count;
 
get_oid(onto, );
shortonto = find_unique_abbrev(, DEFAULT_ABBREV);
 
-   if (!lstat(todo_file, ) && st.st_size == 0 &&
-   write_message("noop\n", 5, todo_file, 0))
-   return -1;
+   if (buf->len == 0)
+   strbuf_add(buf, "noop\n", 5);
+
+   if (todo_list_parse_insn_buffer(buf->buf, todo_list))
+   BUG("unusable todo list");
 
-   if (autosquash && rearrange_squash_in_todo_file())
+   if (autosquash && todo_list_rearrange_squash(todo_list))
return -1;
 
if (cmd && *cmd)
-   sequencer_add_exec_commands(cmd);
+   todo_list_add_exec_commands(todo_list, cmd);
 
-   if (strbuf_read_file(buf, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(buf->buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
-   if (count_commands(_list) == 0) {
+   command_count = count_commands(todo_list);
+   if (command_count == 0) {
apply_autostash(opts);
sequencer_remove_state(opts);
-   todo_list_release(_list);
 
return error(_("nothing to do"));

[PATCH 03/15] sequencer: refactor check_todo_list() to work on a todo_list

2018-10-07 Thread Alban Gruin
This refactors check_todo_list() to work on a todo_list to avoid
redundant reads and writes to the disk.  The function is renamed
todo_list_check().

As rebase -p still need to check the todo list from the disk, a new
function is introduced, check_todo_list_from_file().  It reads the file
from the disk, parses it, pass the todo_list to todo_list_check(), and
writes it back to the disk.

As get_missing_commit_check_level() and the enum
missing_commit_check_level are no longer needed inside of sequencer.c,
they are moved to rebase-interactive.c, and made static again.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |   2 +-
 rebase-interactive.c  | 106 -
 rebase-interactive.h  |   1 +
 sequencer.c   | 109 --
 sequencer.h   |   9 +--
 5 files changed, 120 insertions(+), 107 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index a2ab68ed06..ea1f93ccb6 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -255,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
ret = transform_todos(flags);
break;
case CHECK_TODO_LIST:
-   ret = check_todo_list();
+   ret = check_todo_list_from_file();
break;
case REARRANGE_SQUASH:
ret = rearrange_squash();
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 0f4119cbae..ef8540245d 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -1,8 +1,32 @@
 #include "cache.h"
 #include "commit.h"
-#include "rebase-interactive.h"
 #include "sequencer.h"
+#include "rebase-interactive.h"
 #include "strbuf.h"
+#include "commit-slab.h"
+#include "config.h"
+
+enum missing_commit_check_level {
+   MISSING_COMMIT_CHECK_IGNORE = 0,
+   MISSING_COMMIT_CHECK_WARN,
+   MISSING_COMMIT_CHECK_ERROR
+};
+
+static enum missing_commit_check_level get_missing_commit_check_level(void)
+{
+   const char *value;
+
+   if (git_config_get_value("rebase.missingcommitscheck", ) ||
+   !strcasecmp("ignore", value))
+   return MISSING_COMMIT_CHECK_IGNORE;
+   if (!strcasecmp("warn", value))
+   return MISSING_COMMIT_CHECK_WARN;
+   if (!strcasecmp("error", value))
+   return MISSING_COMMIT_CHECK_ERROR;
+   warning(_("unrecognized setting %s for option "
+ "rebase.missingCommitsCheck. Ignoring."), value);
+   return MISSING_COMMIT_CHECK_IGNORE;
+}
 
 void append_todo_help(unsigned edit_todo, unsigned keep_empty,
  struct strbuf *buf)
@@ -88,3 +112,83 @@ int edit_todo_list(unsigned flags)
 
return 0;
 }
+
+define_commit_slab(commit_seen, unsigned char);
+/*
+ * Check if the user dropped some commits by mistake
+ * Behaviour determined by rebase.missingCommitsCheck.
+ * Check if there is an unrecognized command or a
+ * bad SHA-1 in a command.
+ */
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
+{
+   enum missing_commit_check_level check_level = 
get_missing_commit_check_level();
+   struct strbuf missing = STRBUF_INIT;
+   int advise_to_edit_todo = 0, res = 0, i;
+   struct commit_seen commit_seen;
+
+   init_commit_seen(_seen);
+
+   res = todo_list_parse_insn_buffer(old_todo->buf.buf, old_todo);
+   if (!res)
+   res = todo_list_parse_insn_buffer(new_todo->buf.buf, new_todo);
+   if (res) {
+   advise_to_edit_todo = res;
+   goto leave_check;
+   }
+
+   if (check_level == MISSING_COMMIT_CHECK_IGNORE)
+   goto leave_check;
+
+   /* Mark the commits in git-rebase-todo as seen */
+   for (i = 0; i < new_todo->nr; i++) {
+   struct commit *commit = new_todo->items[i].commit;
+   if (commit)
+   *commit_seen_at(_seen, commit) = 1;
+   }
+
+   /* Find commits in git-rebase-todo.backup yet unseen */
+   for (i = old_todo->nr - 1; i >= 0; i--) {
+   struct todo_item *item = old_todo->items + i;
+   struct commit *commit = item->commit;
+   if (commit && !*commit_seen_at(_seen, commit)) {
+   strbuf_addf(, " - %s %.*s\n",
+   find_unique_abbrev(>object.oid, 
DEFAULT_ABBREV),
+   item->arg_len, item->arg);
+   *commit_seen_at(_seen, commit) = 1;
+   }
+   }
+
+   /* Warn about missing commits */
+   if (!missing.len)
+   goto leave_check;
+
+   if (check_level == MISSING_COMMIT_CHECK_ERROR

[PATCH 09/15] sequencer: refactor skip_unnecessary_picks() to work on a todo_list

2018-10-07 Thread Alban Gruin
This refactors skip_unnecessary_picks() to work on a todo_list.  The
file-handling logic is completely dropped here, as its only usage is
made by complete_action().

Signed-off-by: Alban Gruin 
---
 sequencer.c | 56 +++--
 1 file changed, 16 insertions(+), 40 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index b37935e5ab..a432b64048 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4545,38 +4545,20 @@ static int rewrite_file(const char *path, const char 
*buf, size_t len)
 }
 
 /* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct object_id *output_oid)
+static int skip_unnecessary_picks(struct todo_list *todo_list,
+ struct object_id *output_oid)
 {
-   const char *todo_file = rebase_path_todo();
-   struct strbuf buf = STRBUF_INIT;
-   struct todo_list todo_list = TODO_LIST_INIT;
struct object_id *parent_oid;
int fd, i;
 
-   if (!read_oneliner(, rebase_path_onto(), 0))
-   return error(_("could not read 'onto'"));
-   if (get_oid(buf.buf, output_oid)) {
-   strbuf_release();
-   return error(_("need a HEAD to fixup"));
-   }
-   strbuf_release();
-
-   if (strbuf_read_file_or_whine(_list.buf, todo_file) < 0)
-   return -1;
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
-
-   for (i = 0; i < todo_list.nr; i++) {
-   struct todo_item *item = todo_list.items + i;
+   for (i = 0; i < todo_list->nr; i++) {
+   struct todo_item *item = todo_list->items + i;
 
if (item->command >= TODO_NOOP)
continue;
if (item->command != TODO_PICK)
break;
if (parse_commit(item->commit)) {
-   todo_list_release(_list);
return error(_("could not parse commit '%s'"),
oid_to_hex(>commit->object.oid));
}
@@ -4590,37 +4572,29 @@ static int skip_unnecessary_picks(struct object_id 
*output_oid)
oidcpy(output_oid, >commit->object.oid);
}
if (i > 0) {
-   int offset = get_item_line_offset(_list, i);
+   int offset = get_item_line_offset(todo_list, i);
const char *done_path = rebase_path_done();
 
fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
if (fd < 0) {
error_errno(_("could not open '%s' for writing"),
done_path);
-   todo_list_release(_list);
return -1;
}
-   if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+   if (write_in_full(fd, todo_list->buf.buf, offset) < 0) {
error_errno(_("could not write to '%s'"), done_path);
-   todo_list_release(_list);
close(fd);
return -1;
}
close(fd);
 
-   if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
-todo_list.buf.len - offset) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
+   strbuf_splice(_list->buf, 0, offset, NULL, 0);
 
-   todo_list.current = i;
-   if (is_fixup(peek_command(_list, 0)))
-   record_in_rewritten(output_oid, 
peek_command(_list, 0));
+   todo_list->current = i;
+   if (is_fixup(peek_command(todo_list, 0)))
+   record_in_rewritten(output_oid, peek_command(todo_list, 
0));
}
 
-   todo_list_release(_list);
-
return 0;
 }
 
@@ -4701,6 +4675,11 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
 
todo_list_transform(_todo, flags & ~(TODO_LIST_SHORTEN_IDS));
 
+   if (opts->allow_ff && skip_unnecessary_picks(_todo, )) {
+   todo_list_release(_todo);
+   return error(_("could not skip unnecessary pick commands"));
+   }
+
if (rewrite_file(todo_file, new_todo.buf.buf, new_todo.buf.len) < 0) {
todo_list_release(_todo);
return error_errno(_("could not write '%s'"), todo_file);
@@ -4708,12 +4687,9 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
 
todo_list_release(_todo);
 
-   if (opts->allow_ff && skip_unnecessary_picks())
-   return error(_("could not skip unnecessary pick commands"));
-
if (checkout_onto(opts, onto_name, oid_to_hex(), orig_head))
return -1;
-;
+
if (require_clean_work_tree("rebase", "", 1, 1))
return -1;
 
-- 
2.19.1



[PATCH 02/15] sequencer: make the todo_list structure public

2018-10-07 Thread Alban Gruin
This makes the structures todo_list and todo_item, and the functions
todo_list_release() and parse_insn_buffer(), accessible outside of
sequencer.c.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 66 +
 sequencer.h | 48 ++
 2 files changed, 59 insertions(+), 55 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index ed798b95d1..bb8ca2477f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1443,31 +1443,6 @@ static int allow_empty(struct replay_opts *opts, struct 
commit *commit)
return 1;
 }
 
-/*
- * Note that ordering matters in this enum. Not only must it match the mapping
- * below, it is also divided into several sections that matter.  When adding
- * new commands, make sure you add it in the right section.
- */
-enum todo_command {
-   /* commands that handle commits */
-   TODO_PICK = 0,
-   TODO_REVERT,
-   TODO_EDIT,
-   TODO_REWORD,
-   TODO_FIXUP,
-   TODO_SQUASH,
-   /* commands that do something else than handling a single commit */
-   TODO_EXEC,
-   TODO_LABEL,
-   TODO_RESET,
-   TODO_MERGE,
-   /* commands that do nothing but are counted for reporting progress */
-   TODO_NOOP,
-   TODO_DROP,
-   /* comments (not counted for reporting progress) */
-   TODO_COMMENT
-};
-
 static struct {
char c;
const char *str;
@@ -1937,26 +1912,7 @@ enum todo_item_flags {
TODO_EDIT_MERGE_MSG = 1
 };
 
-struct todo_item {
-   enum todo_command command;
-   struct commit *commit;
-   unsigned int flags;
-   const char *arg;
-   int arg_len;
-   size_t offset_in_buf;
-};
-
-struct todo_list {
-   struct strbuf buf;
-   struct todo_item *items;
-   int nr, alloc, current;
-   int done_nr, total_nr;
-   struct stat_data stat;
-};
-
-#define TODO_LIST_INIT { STRBUF_INIT }
-
-static void todo_list_release(struct todo_list *todo_list)
+void todo_list_release(struct todo_list *todo_list)
 {
strbuf_release(_list->buf);
FREE_AND_NULL(todo_list->items);
@@ -2057,7 +2013,7 @@ static int parse_insn_line(struct todo_item *item, const 
char *bol, char *eol)
return !item->commit;
 }
 
-static int parse_insn_buffer(char *buf, struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list)
 {
struct todo_item *item;
char *p = buf, *next_p;
@@ -2152,7 +2108,7 @@ static int read_populate_todo(struct todo_list *todo_list,
return error(_("could not stat '%s'"), todo_file);
fill_stat_data(_list->stat, );
 
-   res = parse_insn_buffer(todo_list->buf.buf, todo_list);
+   res = todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);
if (res) {
if (is_rebase_i(opts))
return error(_("please fix this using "
@@ -2183,7 +2139,7 @@ static int read_populate_todo(struct todo_list *todo_list,
FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
 
if (strbuf_read_file(, rebase_path_done(), 0) > 0 &&
-   !parse_insn_buffer(done.buf.buf, ))
+   !todo_list_parse_insn_buffer(done.buf.buf, 
))
todo_list->done_nr = count_commands();
else
todo_list->done_nr = 0;
@@ -4429,7 +4385,7 @@ int sequencer_add_exec_commands(const char *commands)
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error(_("could not read '%s'."), todo_file);
 
-   if (parse_insn_buffer(todo_list.buf.buf, _list)) {
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
return error(_("unusable todo list: '%s'"), todo_file);
}
@@ -4485,7 +4441,7 @@ int transform_todos(unsigned flags)
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error(_("could not read '%s'."), todo_file);
 
-   if (parse_insn_buffer(todo_list.buf.buf, _list)) {
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
return error(_("unusable todo list: '%s'"), todo_file);
}
@@ -4571,7 +4527,7 @@ int check_todo_list(void)
goto leave_check;
}
advise_to_edit_todo = res =
-   parse_insn_buffer(todo_list.buf.buf, _list);
+   todo_list_parse_insn_buffer(todo_list.buf.buf, _list);
 
if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
goto leave_check;
@@ -4590,7 +4546,7 @@ int check_todo_list(void)
goto leave_check;
}
strbuf_release(_file);
-   res = !!parse_insn_buffer(todo_list.buf.buf, _list);
+   res = !

[PATCH 10/15] rebase-interactive: use todo_list_transform() in edit_todo_list()

2018-10-07 Thread Alban Gruin
Just like complete_action(), edit_todo_list() used a
function (transform_todo_file()) that read the todo-list from the disk
and wrote it back, resulting in useless disk accesses.

This changes edit_todo_list() to call directly todo_list_transform()
instead.

Signed-off-by: Alban Gruin 
---
 rebase-interactive.c | 40 +++-
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/rebase-interactive.c b/rebase-interactive.c
index 7c7f720a3d..f42d48e192 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -78,39 +78,37 @@ void append_todo_help(unsigned edit_todo, unsigned 
keep_empty,
 
 int edit_todo_list(unsigned flags)
 {
-   struct strbuf buf = STRBUF_INIT;
const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT;
+   int res = 0;
 
-   if (strbuf_read_file(, todo_file, 0) < 0)
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
return error_errno(_("could not read '%s'."), todo_file);
 
-   strbuf_stripspace(, 1);
-   if (write_message(buf.buf, buf.len, todo_file, 0)) {
-   strbuf_release();
-   return -1;
-   }
-
-   strbuf_release();
+   strbuf_stripspace(_list.buf, 1);
+   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
+   todo_list_transform(_list, flags | TODO_LIST_SHORTEN_IDS);
 
-   transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);
-
-   if (strbuf_read_file(, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
+   append_todo_help(1, 0, _list.buf);
 
-   append_todo_help(1, 0, );
-   if (write_message(buf.buf, buf.len, todo_file, 0)) {
-   strbuf_release();
+   if (write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0)) {
+   todo_list_release(_list);
return -1;
}
 
-   strbuf_release();
-
-   if (launch_sequence_editor(todo_file, NULL, NULL))
+   strbuf_reset(_list.buf);
+   if (launch_sequence_editor(todo_file, _list.buf, NULL)) {
+   todo_list_release(_list);
return -1;
+   }
 
-   transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));
+   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_transform(_list, flags & 
~(TODO_LIST_SHORTEN_IDS));
+   res = write_message(todo_list.buf.buf, todo_list.buf.len, 
todo_file, 0);
+   }
 
-   return 0;
+   todo_list_release(_list);
+   return res;
 }
 
 define_commit_slab(commit_seen, unsigned char);
-- 
2.19.1



[PATCH 01/15] sequencer: clear the number of items of a todo_list before parsing

2018-10-07 Thread Alban Gruin
This clears the number of items of a todo_list before parsing it to
allow to parse the same list multiple times without issues.

As its items are not dynamically allocated, or don’t need to allocate
memory, no additionnal memory management is required here.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sequencer.c b/sequencer.c
index 0a3292d5c4..ed798b95d1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2063,6 +2063,8 @@ static int parse_insn_buffer(char *buf, struct todo_list 
*todo_list)
char *p = buf, *next_p;
int i, res = 0, fixup_okay = file_exists(rebase_path_done());
 
+   todo_list->current = todo_list->nr = 0;
+
for (i = 1; *p; i++, p = next_p) {
char *eol = strchrnul(p, '\n');
 
-- 
2.19.1



[PATCH 14/15] sequencer: fix a call to error() in transform_todo_file()

2018-10-07 Thread Alban Gruin
This replaces a call to error() by a call to error_errno() after writing
the content of the todo list to the disk in transform_todo_file().

Signed-off-by: Alban Gruin 
---
 sequencer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sequencer.c b/sequencer.c
index 93b9b40f66..65bf251ba5 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4421,7 +4421,7 @@ int sequencer_add_exec_commands(const char *commands)
int res;
 
if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
+   return error_errno(_("could not read '%s'."), todo_file);
 
if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
todo_list_release(_list);
-- 
2.19.1



[PATCH 00/15] sequencer: refactor functions working on a todo_list

2018-10-07 Thread Alban Gruin
At the center of the "interactive" part of the interactive rebase lies
the todo list.  When the user starts an interactive rebase, a todo list
is generated, presented to the user (who then edits it using a text
editor), read back, and then is checked and processed before the actual
rebase takes place.

Some of this processing includes adding execs commands, reordering
fixup! and squash! commits, and checking if no commits were accidentally
dropped by the user.

Before I converted the interactive rebase in C, these functions were
called by git-rebase--interactive.sh through git-rebase--helper.  Since
the only way to pass around a large amount of data between a shell
script and a C program is to use a file (or any declination of a file),
the functions that checked and processed the todo list were directly
working on a file, the same file that the user edited.

During the conversion, I did not address this issue, which lead to a
complete_action() that reads the todo list file, does some computation
based on its content, and writes it back to the disk, several times in
the same function.

As it is not an efficient way to handle a data structure, this patch
series refactor the functions that processes the todo list to work on a
todo_list structure instead of reading it from the disk.

Some commits consists in modifying edit_todo_list() (initially used by
--edit-todo) to handle the initial edition of the todo list, to increase
code sharing.

Alban Gruin (15):
  sequencer: clear the number of items of a todo_list before parsing
  sequencer: make the todo_list structure public
  sequencer: refactor check_todo_list() to work on a todo_list
  sequencer: refactor sequencer_add_exec_commands() to work on a
todo_list
  sequencer: refactor rearrange_squash() to work on a todo_list
  sequencer: refactor transform_todos() to work on a todo_list
  sequencer: make sequencer_make_script() write its script to a strbuf
  sequencer: change complete_action() to use the refactored functions
  sequencer: refactor skip_unnecessary_picks() to work on a todo_list
  rebase-interactive: use todo_list_transform() in edit_todo_list()
  rebase-interactive: append_todo_help() changes
  rebase-interactive: rewrite edit_todo_list() to handle the initial
edit
  sequencer: use edit_todo_list() in complete_action()
  sequencer: fix a call to error() in transform_todo_file()
  rebase--interactive: move transform_todo_file() to
rebase--interactive.c

 builtin/rebase--interactive.c |  65 +++--
 rebase-interactive.c  | 161 ++--
 rebase-interactive.h  |   8 +-
 sequencer.c   | 479 --
 sequencer.h   |  66 -
 5 files changed, 406 insertions(+), 373 deletions(-)

-- 
2.19.1



[PATCH 11/15] rebase-interactive: append_todo_help() changes

2018-10-07 Thread Alban Gruin
This moves the writing of the comment "Rebase $shortrevisions onto
$shortonto ($command_count commands)" from complete_action() to
append_todo_help().

shortrevisions, shortonto, and command_count are passed as parameters to
append_todo_help().

During the initial edit of the todo list, shortrevisions and shortonto
are not NULL.  Therefore, if shortrevisions or shortonto is NULL, then
edit_todo would be true, otherwise it would be false.  Thus, edit_todo
is removed from the parameters of append_todo_help().

edit_todo_list() and complete_action() are modified to fit these
changes.

Signed-off-by: Alban Gruin 
---
 rebase-interactive.c | 14 --
 rebase-interactive.h |  3 ++-
 sequencer.c  |  8 ++--
 3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/rebase-interactive.c b/rebase-interactive.c
index f42d48e192..7168d56d17 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -28,7 +28,8 @@ static enum missing_commit_check_level 
get_missing_commit_check_level(void)
return MISSING_COMMIT_CHECK_IGNORE;
 }
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
  struct strbuf *buf)
 {
const char *msg = _("\nCommands:\n"
@@ -47,6 +48,15 @@ void append_todo_help(unsigned edit_todo, unsigned 
keep_empty,
 ".   specified). Use -c  to reword the commit message.\n"
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
+   unsigned edit_todo = !(shortrevisions && shortonto);
+
+   if (!edit_todo) {
+   strbuf_addch(buf, '\n');
+   strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+ "Rebase %s onto %s (%d commands)",
+ command_count),
+ shortrevisions, shortonto, command_count);
+   }
 
strbuf_add_commented_lines(buf, msg, strlen(msg));
 
@@ -89,7 +99,7 @@ int edit_todo_list(unsigned flags)
if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
todo_list_transform(_list, flags | TODO_LIST_SHORTEN_IDS);
 
-   append_todo_help(1, 0, _list.buf);
+   append_todo_help(flags, 0, NULL, NULL, _list.buf);
 
if (write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0)) {
todo_list_release(_list);
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 6bc7bc315d..61858f3a02 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -1,7 +1,8 @@
 #ifndef REBASE_INTERACTIVE_H
 #define REBASE_INTERACTIVE_H
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+ const char *shortrevisions, const char *shortonto,
  struct strbuf *buf);
 int edit_todo_list(unsigned flags);
 int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
diff --git a/sequencer.c b/sequencer.c
index a432b64048..94d3058359 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4636,12 +4636,8 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
 
todo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);
 
-   strbuf_addch(buf, '\n');
-   strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
- "Rebase %s onto %s (%d commands)",
- command_count),
- shortrevisions, shortonto, command_count);
-   append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
+   append_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,
+shortrevisions, shortonto, buf);
 
if (write_message(buf->buf, buf->len, todo_file, 0))
return error_errno(_("could not write '%s'"), todo_file);
-- 
2.19.1



[PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list

2018-10-07 Thread Alban Gruin
This refactors sequencer_add_exec_commands() to work on a todo_list to
avoid redundant reads and writes to the disk.

sequencer_add_exec_commands() still reads the todo list from the disk,
as it is needed by rebase -p.  todo_list_add_exec_commands() works on a
todo_list structure, and reparses it at the end.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 56 +++--
 1 file changed, 33 insertions(+), 23 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 8dda61927c..6d998f21a4 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4370,34 +4370,21 @@ int sequencer_make_script(FILE *out, int argc, const 
char **argv,
return 0;
 }
 
-/*
- * Add commands after pick and (series of) squash/fixup commands
- * in the todo list.
- */
-int sequencer_add_exec_commands(const char *commands)
+static void todo_list_add_exec_commands(struct todo_list *todo_list,
+   const char *commands)
 {
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   struct strbuf *buf = _list.buf;
+   struct strbuf *buf = _list->buf;
size_t offset = 0, commands_len = strlen(commands);
int i, insert;
 
-   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error(_("could not read '%s'."), todo_file);
-
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_release(_list);
-   return error(_("unusable todo list: '%s'"), todo_file);
-   }
-
/*
 * Insert  after every pick. Here, fixup/squash chains
 * are considered part of the pick, so we insert the commands *after*
 * those chains if there are any.
 */
insert = -1;
-   for (i = 0; i < todo_list.nr; i++) {
-   enum todo_command command = todo_list.items[i].command;
+   for (i = 0; i < todo_list->nr; i++) {
+   enum todo_command command = todo_list->items[i].command;
 
if (insert >= 0) {
/* skip fixup/squash chains */
@@ -4408,7 +4395,7 @@ int sequencer_add_exec_commands(const char *commands)
continue;
}
strbuf_insert(buf,
- todo_list.items[insert].offset_in_buf +
+ todo_list->items[insert].offset_in_buf +
  offset, commands, commands_len);
offset += commands_len;
insert = -1;
@@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char *commands)
}
 
/* insert or append final  */
-   if (insert >= 0 && insert < todo_list.nr)
-   strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
+   if (insert >= 0 && insert < todo_list->nr)
+   strbuf_insert(buf, todo_list->items[insert].offset_in_buf +
  offset, commands, commands_len);
else if (insert >= 0 || !offset)
strbuf_add(buf, commands, commands_len);
 
-   i = write_message(buf->buf, buf->len, todo_file, 0);
+   if (todo_list_parse_insn_buffer(buf->buf, todo_list))
+   BUG("unusable todo list");}
+
+/*
+ * 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 res;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error(_("could not read '%s'."), todo_file);
+
+   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
+   todo_list_release(_list);
+   return error(_("unusable todo list: '%s'"), todo_file);
+   }
+
+   todo_list_add_exec_commands(_list, commands);
+   res = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);
todo_list_release(_list);
-   return i;
+
+   return res;
 }
 
 int transform_todos(unsigned flags)
-- 
2.19.1



[PATCH 05/15] sequencer: refactor rearrange_squash() to work on a todo_list

2018-10-07 Thread Alban Gruin
This refactors rearrange_squash() to work on a todo_list to avoid
redundant reads and writes.  The function is renamed
todo_list_rearrange_squash().

As rebase -p still need to check the todo list from the disk, a new
function is introduced, rearrange_squash_in_todo_file().

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c |  2 +-
 sequencer.c   | 73 +--
 sequencer.h   |  2 +-
 3 files changed, 46 insertions(+), 31 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index ea1f93ccb6..8deef126d1 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -258,7 +258,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
ret = check_todo_list_from_file();
break;
case REARRANGE_SQUASH:
-   ret = rearrange_squash();
+   ret = rearrange_squash_in_todo_file();
break;
case ADD_EXEC:
ret = sequencer_add_exec_commands(cmd);
diff --git a/sequencer.c b/sequencer.c
index 6d998f21a4..8a6176b3d0 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4632,7 +4632,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
write_message("noop\n", 5, todo_file, 0))
return -1;
 
-   if (autosquash && rearrange_squash())
+   if (autosquash && rearrange_squash_in_todo_file())
return -1;
 
if (cmd && *cmd)
@@ -4738,22 +4738,13 @@ define_commit_slab(commit_todo_item, struct todo_item 
*);
  * message will have to be retrieved from the commit (as the oneline in the
  * script cannot be trusted) in order to normalize the autosquash arrangement.
  */
-int rearrange_squash(void)
+static int todo_list_rearrange_squash(struct todo_list *todo_list)
 {
-   const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
struct hashmap subject2item;
-   int res = 0, rearranged = 0, *next, *tail, i;
+   int rearranged = 0, *next, *tail, i;
char **subjects;
struct commit_todo_item commit_todo;
 
-   if (strbuf_read_file_or_whine(_list.buf, todo_file) < 0)
-   return -1;
-   if (todo_list_parse_insn_buffer(todo_list.buf.buf, _list) < 0) {
-   todo_list_release(_list);
-   return -1;
-   }
-
init_commit_todo_item(_todo);
/*
 * The hashmap maps onelines to the respective todo list index.
@@ -4765,13 +4756,13 @@ int rearrange_squash(void)
 * be moved to appear after the i'th.
 */
hashmap_init(, (hashmap_cmp_fn) subject2item_cmp,
-NULL, todo_list.nr);
-   ALLOC_ARRAY(next, todo_list.nr);
-   ALLOC_ARRAY(tail, todo_list.nr);
-   ALLOC_ARRAY(subjects, todo_list.nr);
-   for (i = 0; i < todo_list.nr; i++) {
+NULL, todo_list->nr);
+   ALLOC_ARRAY(next, todo_list->nr);
+   ALLOC_ARRAY(tail, todo_list->nr);
+   ALLOC_ARRAY(subjects, todo_list->nr);
+   for (i = 0; i < todo_list->nr; i++) {
struct strbuf buf = STRBUF_INIT;
-   struct todo_item *item = todo_list.items + i;
+   struct todo_item *item = todo_list->items + i;
const char *commit_buffer, *subject, *p;
size_t subject_len;
int i2 = -1;
@@ -4784,7 +4775,6 @@ int rearrange_squash(void)
}
 
if (is_fixup(item->command)) {
-   todo_list_release(_list);
clear_commit_todo_item(_todo);
return error(_("the script was already rearranged."));
}
@@ -4819,7 +4809,7 @@ int rearrange_squash(void)
 *commit_todo_item_at(_todo, commit2))
/* found by commit name */
i2 = *commit_todo_item_at(_todo, commit2)
-   - todo_list.items;
+   - todo_list->items;
else {
/* copy can be a prefix of the commit subject */
for (i2 = 0; i2 < i; i2++)
@@ -4832,7 +4822,7 @@ int rearrange_squash(void)
}
if (i2 >= 0) {
rearranged = 1;
-   todo_list.items[i].command =
+   todo_list->items[i].command =
starts_with(subject, "fixup!") ?
TODO_FIXUP : TODO_SQUASH;
if (next[i2] < 0)
@@ -4852,8 +4842,8 @@ int rearrange_squash(void)
if (rearranged) {
struct strbuf buf = STRBUF_INIT;
 
-   for (i = 0; i

[PATCH 13/15] sequencer: use edit_todo_list() in complete_action()

2018-10-07 Thread Alban Gruin
This changes complete_action() to use edit_todo_list(), now that it can
handle the initial edit of the todo list.

Signed-off-by: Alban Gruin 
---
 sequencer.c | 27 +++
 1 file changed, 7 insertions(+), 20 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index bfcbe8239b..93b9b40f66 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4608,7 +4608,7 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
struct todo_list new_todo = TODO_LIST_INIT;
struct strbuf *buf = _list->buf;
struct object_id oid;
-   int command_count;
+   int command_count, res;
 
get_oid(onto, );
shortonto = find_unique_abbrev(, DEFAULT_ABBREV);
@@ -4633,27 +4633,16 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
return error(_("nothing to do"));
}
 
-   todo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);
-
-   append_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,
-shortrevisions, shortonto, buf);
-
-   if (write_message(buf->buf, buf->len, todo_file, 0))
-   return error_errno(_("could not write '%s'"), todo_file);
-
-   if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
-   return error(_("could not copy '%s' to '%s'."), todo_file,
-rebase_path_todo_backup());
-
-   if (launch_sequence_editor(todo_file, _todo.buf, NULL)) {
+   res = edit_todo_list(todo_list, _todo, flags,
+command_count, shortrevisions, shortonto);
+   if (res == -1)
+   return -1;
+   else if (res == -2) {
apply_autostash(opts);
sequencer_remove_state(opts);
 
return -1;
-   }
-
-   strbuf_stripspace(_todo.buf, 1);
-   if (new_todo.buf.len == 0) {
+   } else if (res == -3) {
apply_autostash(opts);
sequencer_remove_state(opts);
todo_list_release(_todo);
@@ -4668,8 +4657,6 @@ int complete_action(struct replay_opts *opts, unsigned 
flags,
return -1;
}
 
-   todo_list_transform(_todo, flags & ~(TODO_LIST_SHORTEN_IDS));
-
if (opts->allow_ff && skip_unnecessary_picks(_todo, )) {
todo_list_release(_todo);
return error(_("could not skip unnecessary pick commands"));
-- 
2.19.1



[PATCH 12/15] rebase-interactive: rewrite edit_todo_list() to handle the initial edit

2018-10-07 Thread Alban Gruin
edit_todo_list() is changed to work on a todo_list, and to handle the
initial edition of the todo list (ie. making a backup of the todo
list).

It does not check for dropped commits yet, as todo_list_check() does not
work if the old todo list has invalid commands.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive.c | 22 +++-
 rebase-interactive.c  | 49 ++-
 rebase-interactive.h  |  4 ++-
 sequencer.c   |  3 +--
 sequencer.h   |  1 +
 5 files changed, 51 insertions(+), 28 deletions(-)

diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c
index 0700339f90..264e940b47 100644
--- a/builtin/rebase--interactive.c
+++ b/builtin/rebase--interactive.c
@@ -13,6 +13,26 @@ static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 
+static int edit_todo_file(unsigned flags)
+{
+   const char *todo_file = rebase_path_todo();
+   struct todo_list todo_list = TODO_LIST_INIT,
+   new_todo = TODO_LIST_INIT;
+
+   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   strbuf_stripspace(_list.buf, 1);
+   if (!edit_todo_list(_list, _todo, flags, 0, NULL, NULL) &&
+   write_message(new_todo.buf.buf, new_todo.buf.len, todo_file, 0) < 0)
+   return error_errno(_("could not write '%s'"), todo_file);
+
+   todo_list_release(_list);
+   todo_list_release(_todo);
+
+   return 0;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
   char **revisions, char **shortrevisions)
@@ -231,7 +251,7 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
break;
}
case EDIT_TODO:
-   ret = edit_todo_list(flags);
+   ret = edit_todo_file(flags);
break;
case SHOW_CURRENT_PATCH: {
struct child_process cmd = CHILD_PROCESS_INIT;
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 7168d56d17..6ee60ac03f 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -86,39 +86,40 @@ void append_todo_help(unsigned keep_empty, int 
command_count,
}
 }
 
-int edit_todo_list(unsigned flags)
+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,
+  unsigned flags, int command_count,
+  const char *shortrevisions, const char *shortonto)
 {
const char *todo_file = rebase_path_todo();
-   struct todo_list todo_list = TODO_LIST_INIT;
-   int res = 0;
+   unsigned initial = shortrevisions && shortonto;
 
-   if (strbuf_read_file(_list.buf, todo_file, 0) < 0)
-   return error_errno(_("could not read '%s'."), todo_file);
+   if (initial || !todo_list_parse_insn_buffer(todo_list->buf.buf, 
todo_list))
+   todo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);
 
-   strbuf_stripspace(_list.buf, 1);
-   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list))
-   todo_list_transform(_list, flags | TODO_LIST_SHORTEN_IDS);
+   if (initial)
+   append_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,
+shortrevisions, shortonto, _list->buf);
+   else
+   append_todo_help(flags, 0, NULL, NULL, _list->buf);
 
-   append_todo_help(flags, 0, NULL, NULL, _list.buf);
+   if (write_message(todo_list->buf.buf, todo_list->buf.len, todo_file, 0))
+   return error_errno(_("could not write '%s''"), todo_file);
 
-   if (write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0)) {
-   todo_list_release(_list);
-   return -1;
-   }
+   if (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))
+   return error(_("could not copy '%s' to '%s'."), todo_file,
+rebase_path_todo_backup());
 
-   strbuf_reset(_list.buf);
-   if (launch_sequence_editor(todo_file, _list.buf, NULL)) {
-   todo_list_release(_list);
-   return -1;
-   }
+   if (launch_sequence_editor(todo_file, _todo->buf, NULL))
+   return -2;
 
-   if (!todo_list_parse_insn_buffer(todo_list.buf.buf, _list)) {
-   todo_list_transform(_list, flags & 
~(TODO_LIST_SHORTEN_IDS));
-   res = write_message(todo_list.buf.buf, todo_list.buf.len, 
todo_file, 0);
-   }
+   strbuf_stripspace(_todo->buf, 1);
+   if (initial && new_todo->buf.l

[GSoC][PATCH v8 10/20] t3404: todo list with commented-out commands only aborts

2018-09-27 Thread Alban Gruin
If the todo list generated by `--make-script` is empty,
complete_action() writes a noop, but if it has only commented-out
commands, it will abort with the message "Nothing to do", and does not
launch the editor.  This adds a new test to ensure that
complete_action() behaves this way.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 t/t3404-rebase-interactive.sh | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ff89b6341a..a7fc3cd5be 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -75,6 +75,16 @@ test_expect_success 'rebase --keep-empty' '
test_line_count = 6 actual
 '
 
+cat > expect <actual 
2>&1 &&
+   test_i18ncmp expect actual
+'
+
 test_expect_success 'rebase -i with the exec command' '
git checkout master &&
(
-- 
2.19.0



[GSoC][PATCH v8 18/20] rebase--interactive2: rewrite the submodes of interactive rebase in C

2018-09-27 Thread Alban Gruin
This rewrites the submodes of interactive rebase (`--continue`,
`--skip`, `--edit-todo`, and `--show-current-patch`) in C.

git-rebase.sh is then modified to call directly git-rebase--interactive2
instead of git-rebase--interactive.sh.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--interactive2.c | 51 ++
 git-rebase.sh  | 45 +++---
 2 files changed, 86 insertions(+), 10 deletions(-)

diff --git a/builtin/rebase--interactive2.c b/builtin/rebase--interactive2.c
index 45336073a8..aaf13c9621 100644
--- a/builtin/rebase--interactive2.c
+++ b/builtin/rebase--interactive2.c
@@ -134,11 +134,14 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
-   int abbreviate_commands = 0, rebase_cousins = -1;
+   int abbreviate_commands = 0, rebase_cousins = -1, ret = 0;
const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
*squash_onto = NULL, *upstream = NULL, *head_name = NULL,
*switch_to = NULL, *cmd = NULL;
char *raw_strategies = NULL;
+   enum {
+   NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH
+   } command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
OPT_BOOL(0, "keep-empty", _empty, N_("keep empty 
commits")),
@@ -151,6 +154,13 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
 N_("move commits that begin with squash!/fixup!")),
OPT_BOOL(0, "signoff", , N_("sign commits")),
OPT__VERBOSE(, N_("be verbose")),
+   OPT_CMDMODE(0, "continue", , N_("continue rebase"),
+   CONTINUE),
+   OPT_CMDMODE(0, "skip", , N_("skip commit"), SKIP),
+   OPT_CMDMODE(0, "edit-todo", , N_("edit the todo list"),
+   EDIT_TODO),
+   OPT_CMDMODE(0, "show-current-patch", , N_("show the 
current patch"),
+   SHOW_CURRENT_PATCH),
OPT_STRING(0, "onto", , N_("onto"), N_("onto")),
OPT_STRING(0, "restrict-revision", _revision,
   N_("restrict-revision"), N_("restrict revision")),
@@ -198,10 +208,39 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
warning(_("--[no-]rebase-cousins has no effect without "
  "--rebase-merges"));
 
-   if (!onto && !upstream)
-   die(_("a base commit must be provided with --upstream or 
--onto"));
+   switch (command) {
+   case NONE:
+   if (!onto && !upstream)
+   die(_("a base commit must be provided with --upstream 
or --onto"));
+
+   ret = do_interactive_rebase(, flags, switch_to, upstream, 
onto,
+   onto_name, squash_onto, head_name, 
restrict_revision,
+   raw_strategies, cmd, autosquash);
+   break;
+   case SKIP: {
+   struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+   rerere_clear(_rr);
+   /* fallthrough */
+   case CONTINUE:
+   ret = sequencer_continue();
+   break;
+   }
+   case EDIT_TODO:
+   ret = edit_todo_list(flags);
+   break;
+   case SHOW_CURRENT_PATCH: {
+   struct child_process cmd = CHILD_PROCESS_INIT;
+
+   cmd.git_cmd = 1;
+   argv_array_pushl(, "show", "REBASE_HEAD", "--", NULL);
+   ret = run_command();
+
+   break;
+   }
+   default:
+   BUG("invalid command '%d'", command);
+   }
 
-   return !!do_interactive_rebase(, flags, switch_to, upstream, onto,
-  onto_name, squash_onto, head_name, 
restrict_revision,
-  raw_strategies, cmd, autosquash);
+   return !!ret;
 }
diff --git a/git-rebase.sh b/git-rebase.sh
index 51a6db7daa..6e1e413cf2 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -200,19 +200,56 @@ finish_rebase () {
rm -rf "$state_dir"
 }
 
+run_interactive () {
+   GIT_CHERRY_PICK_HELP="$resolvemsg"
+   export GIT_CHERRY_PICK_HELP
+
+   test -n "$keep_empty" && keep_empty="--keep-empty"
+   test -n &

[GSoC][PATCH v8 11/20] rebase -i: rewrite complete_action() in C

2018-09-27 Thread Alban Gruin
This rewrites complete_action() from shell to C.

A new mode is added to rebase--helper (`--complete-action`), as well as
a new flag (`--autosquash`).

Finally, complete_action() is stripped from git-rebase--interactive.sh.

The original complete_action() would return the code 2 when the todo
list contained no actions.  This was a special case for rebase -i and
-p; git-rebase.sh would then apply the autostash, delete the state
directory, and die with the message "Nothing to do".  This cleanup is
rewritten in C instead of returning 2.  As rebase -i no longer returns
2, the comment describing this behaviour in git-rebase.sh is updated to
reflect this change.

The message "Nothing to do" is now printed with error(), and so becomes
"error: nothing to do".  Some tests in t3404 check this value, so they
are updated to fit this change.

The first check might seem useless as we write "noop" to the todo list
if it is empty.  Actually, the todo list might contain commented
commands (ie. empty commits).  In this case, complete_action() won’t
write "noop", and will abort without starting the editor.

Signed-off-by: Alban Gruin 
---
The changes are due to conflict resolution, no real changes otherwise.

 builtin/rebase--helper.c  |  12 +++-
 git-rebase--interactive.sh|  53 ++---
 git-rebase.sh |   2 +-
 sequencer.c   | 104 ++
 sequencer.h   |   4 ++
 t/t3404-rebase-interactive.sh |   2 +-
 6 files changed, 124 insertions(+), 53 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index bed3dd2b95..01b958 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -13,13 +13,13 @@ static const char * const builtin_rebase_helper_usage[] = {
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
-   unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
+   unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
int abbreviate_commands = 0, rebase_cousins = -1;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH,
-   CHECKOUT_ONTO
+   CHECKOUT_ONTO, COMPLETE_ACTION
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -29,6 +29,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
OPT_BOOL(0, "rebase-merges", _merges, N_("rebase merge 
commits")),
OPT_BOOL(0, "rebase-cousins", _cousins,
 N_("keep original branch points of cousins")),
+   OPT_BOOL(0, "autosquash", ,
+N_("move commits that begin with squash!/fixup!")),
OPT__VERBOSE(, N_("be verbose")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
@@ -57,6 +59,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
OPT_CMDMODE(0, "checkout-onto", ,
N_("checkout a commit"), CHECKOUT_ONTO),
+   OPT_CMDMODE(0, "complete-action", ,
+   N_("complete the action"), COMPLETE_ACTION),
OPT_END()
};
 
@@ -110,5 +114,9 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!prepare_branch_to_be_rebased(, argv[1]);
if (command == CHECKOUT_ONTO && argc == 4)
return !!checkout_onto(, argv[1], argv[2], argv[3]);
+   if (command == COMPLETE_ACTION && argc == 6)
+   return !!complete_action(, flags, argv[1], argv[2], 
argv[3],
+argv[4], argv[5], autosquash);
+
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index b68f108f28..59dc4888a6 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -127,54 +127,6 @@ init_revisions_and_shortrevisions () {
fi
 }
 
-complete_action() {
-   test -s "$todo" || echo noop >> "$todo"
-   test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
-   test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
-
-   todocount=$(git stripspace --strip-comments <&

[GSoC][PATCH v8 09/20] sequencer: change the way skip_unnecessary_picks() returns its result

2018-09-27 Thread Alban Gruin
Instead of skip_unnecessary_picks() printing its result to stdout, it
returns it into a struct object_id, as the rewrite of complete_action()
(to come in the next commit) will need it.

rebase--helper then is modified to fit this change.

Signed-off-by: Alban Gruin 
---
The changes are due to conflict resolution, no real changes otherwise.

 builtin/rebase--helper.c | 10 --
 sequencer.c  | 13 ++---
 sequencer.h  |  2 +-
 3 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 313092c465..bed3dd2b95 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -90,8 +90,14 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!transform_todos(flags);
if (command == CHECK_TODO_LIST && argc == 1)
return !!check_todo_list();
-   if (command == SKIP_UNNECESSARY_PICKS && argc == 1)
-   return !!skip_unnecessary_picks();
+   if (command == SKIP_UNNECESSARY_PICKS && argc == 1) {
+   struct object_id oid;
+   int ret = skip_unnecessary_picks();
+
+   if (!ret)
+   puts(oid_to_hex());
+   return !!ret;
+   }
if (command == REARRANGE_SQUASH && argc == 1)
return !!rearrange_squash();
if (command == ADD_EXEC && argc == 2)
diff --git a/sequencer.c b/sequencer.c
index c7f930ab9c..f2cda5593d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4593,17 +4593,17 @@ static int rewrite_file(const char *path, const char 
*buf, size_t len)
 }
 
 /* skip picking commits whose parents are unchanged */
-int skip_unnecessary_picks(void)
+int skip_unnecessary_picks(struct object_id *output_oid)
 {
const char *todo_file = rebase_path_todo();
struct strbuf buf = STRBUF_INIT;
struct todo_list todo_list = TODO_LIST_INIT;
-   struct object_id onto_oid, *oid = _oid, *parent_oid;
+   struct object_id *parent_oid;
int fd, i;
 
if (!read_oneliner(, rebase_path_onto(), 0))
return error(_("could not read 'onto'"));
-   if (get_oid(buf.buf, _oid)) {
+   if (get_oid(buf.buf, output_oid)) {
strbuf_release();
return error(_("need a HEAD to fixup"));
}
@@ -4633,9 +4633,9 @@ int skip_unnecessary_picks(void)
if (item->commit->parents->next)
break; /* merge commit */
parent_oid = >commit->parents->item->object.oid;
-   if (!oideq(parent_oid, oid))
+   if (!oideq(parent_oid, output_oid))
break;
-   oid = >commit->object.oid;
+   oidcpy(output_oid, >commit->object.oid);
}
if (i > 0) {
int offset = get_item_line_offset(_list, i);
@@ -4664,11 +4664,10 @@ int skip_unnecessary_picks(void)
 
todo_list.current = i;
if (is_fixup(peek_command(_list, 0)))
-   record_in_rewritten(oid, peek_command(_list, 0));
+   record_in_rewritten(output_oid, 
peek_command(_list, 0));
}
 
todo_list_release(_list);
-   printf("%s\n", oid_to_hex(oid));
 
return 0;
 }
diff --git a/sequencer.h b/sequencer.h
index 5fa89edd3b..6b0885db57 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -96,7 +96,7 @@ int sequencer_add_exec_commands(const char *command);
 int transform_todos(unsigned flags);
 enum missing_commit_check_level get_missing_commit_check_level(void);
 int check_todo_list(void);
-int skip_unnecessary_picks(void);
+int skip_unnecessary_picks(struct object_id *output_oid);
 int rearrange_squash(void);
 
 extern const char sign_off_header[];
-- 
2.19.0



[GSoC][PATCH v8 13/20] rebase -i: implement the logic to initialize $revisions in C

2018-09-27 Thread Alban Gruin
This rewrites the part of init_revisions_and_shortrevisions() needed by
`--make-script` from shell to C.  The new version is called
get_revision_ranges(), and is a static function inside of
rebase--helper.c.  As this does not initialize $shortrevision, the
original shell version is not yet stripped.

Unlike init_revisions_and_shortrevisions(), get_revision_ranges()
doesn’t write $squash_onto to the state directory, it’s done by the
handler of `--make-script` instead.

Finally, this drops the $revision argument passed to `--make-script` in
git-rebase--interactive.sh, and rebase--helper is changed accordingly.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   | 56 --
 git-rebase--interactive.sh |  4 ++-
 2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index e1460136f5..acc71a6f99 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -4,6 +4,25 @@
 #include "parse-options.h"
 #include "sequencer.h"
 #include "rebase-interactive.h"
+#include "argv-array.h"
+
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+
+static int get_revision_ranges(const char *upstream, const char *onto,
+  const char **head_hash,
+  char **revisions)
+{
+   const char *base_rev = upstream ? upstream : onto;
+   struct object_id orig_head;
+
+   if (get_oid("HEAD", _head))
+   return error(_("no HEAD?"));
+
+   *head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
+   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+
+   return 0;
+}
 
 static const char * const builtin_rebase_helper_usage[] = {
N_("git rebase--helper []"),
@@ -14,7 +33,9 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
-   int abbreviate_commands = 0, rebase_cousins = -1;
+   int abbreviate_commands = 0, rebase_cousins = -1, ret;
+   const char *head_hash = NULL, *onto = NULL, *restrict_revision = NULL,
+   *squash_onto = NULL, *upstream = NULL;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC, EDIT_TODO, 
PREPARE_BRANCH,
@@ -54,6 +75,13 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
OPT_CMDMODE(0, "complete-action", ,
N_("complete the action"), COMPLETE_ACTION),
+   OPT_STRING(0, "onto", , N_("onto"), N_("onto")),
+   OPT_STRING(0, "restrict-revision", _revision,
+  N_("restrict-revision"), N_("restrict revision")),
+   OPT_STRING(0, "squash-onto", _onto, N_("squash-onto"),
+  N_("squash onto")),
+   OPT_STRING(0, "upstream", , N_("upstream"),
+  N_("the upstream commit")),
OPT_END()
};
 
@@ -81,8 +109,30 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!sequencer_continue();
if (command == ABORT && argc == 1)
return !!sequencer_remove_state();
-   if (command == MAKE_SCRIPT && argc > 1)
-   return !!sequencer_make_script(stdout, argc, argv, flags);
+   if (command == MAKE_SCRIPT && argc == 1) {
+   char *revisions = NULL;
+   struct argv_array make_script_args = ARGV_ARRAY_INIT;
+
+   if (!upstream && squash_onto)
+   write_file(path_squash_onto(), "%s\n", squash_onto);
+
+   ret = get_revision_ranges(upstream, onto, _hash, 
);
+   if (ret)
+   return ret;
+
+   argv_array_pushl(_script_args, "", revisions, NULL);
+   if (restrict_revision)
+   argv_array_push(_script_args, restrict_revision);
+
+   ret = sequencer_make_script(stdout,
+   make_script_args.argc, 
make_script_args.argv,
+   flags);
+
+   free(revisions);
+   argv_array_clear(_script_args);
+
+   return !!ret;
+   }
if ((command == SHORTEN_OIDS || command == EXPAND_OIDS) && argc == 1)
return !!transform_todos(flags);
if (command == CHECK_TODO_L

[GSoC][PATCH v8 14/20] rebase -i: rewrite the rest of init_revisions_and_shortrevisions() in C

2018-09-27 Thread Alban Gruin
This rewrites the part of init_revisions_and_shortrevisions() needed by
`--complete-action` (which initialize $shortrevisions) from shell to C.

When `upstream` is empty, it means that the user launched a `rebase
--root`, and `onto` contains the ID of an empty commit.  As a range
between an empty commit and `head` is not really meaningful, `onto` is
not used to initialize `shortrevisions` in this case.

The corresponding arguments passed to `--complete-action` are then
dropped, and init_revisions_and_shortrevisions() is stripped from
git-rebase--interactive.sh

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   | 40 --
 git-rebase--interactive.sh | 27 -
 2 files changed, 38 insertions(+), 29 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index acc71a6f99..0716bbfd78 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -10,7 +10,7 @@ static GIT_PATH_FUNC(path_squash_onto, 
"rebase-merge/squash-onto")
 
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
-  char **revisions)
+  char **revisions, char **shortrevisions)
 {
const char *base_rev = upstream ? upstream : onto;
struct object_id orig_head;
@@ -19,7 +19,25 @@ static int get_revision_ranges(const char *upstream, const 
char *onto,
return error(_("no HEAD?"));
 
*head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
-   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+
+   if (revisions)
+   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+   if (shortrevisions) {
+   const char *shorthead;
+
+   shorthead = find_unique_abbrev(_head, DEFAULT_ABBREV);
+
+   if (upstream) {
+   const char *shortrev;
+   struct object_id rev_oid;
+
+   get_oid(base_rev, _oid);
+   shortrev = find_unique_abbrev(_oid, DEFAULT_ABBREV);
+
+   *shortrevisions = xstrfmt("%s..%s", shortrev, 
shorthead);
+   } else
+   *shortrevisions = xstrdup(shorthead);
+   }
 
return 0;
 }
@@ -116,7 +134,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   ret = get_revision_ranges(upstream, onto, _hash, 
);
+   ret = get_revision_ranges(upstream, onto, _hash, 
, NULL);
if (ret)
return ret;
 
@@ -145,9 +163,19 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
return !!prepare_branch_to_be_rebased(, argv[1]);
-   if (command == COMPLETE_ACTION && argc == 6)
-   return !!complete_action(, flags, argv[1], argv[2], 
argv[3],
-argv[4], argv[5], autosquash);
+   if (command == COMPLETE_ACTION && argc == 3) {
+   char *shortrevisions = NULL;
+
+   ret = get_revision_ranges(upstream, onto, _hash, NULL, 
);
+   if (ret)
+   return ret;
+
+   ret = complete_action(, flags, shortrevisions, argv[1], 
onto,
+ head_hash, argv[2], autosquash);
+
+   free(shortrevisions);
+   return !!ret;
+   }
 
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 4ca47aed1e..08e9a21c2f 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -60,23 +60,6 @@ init_basic_state () {
write_basic_state
 }
 
-init_revisions_and_shortrevisions () {
-   shorthead=$(git rev-parse --short $orig_head)
-   shortonto=$(git rev-parse --short $onto)
-   if test -z "$rebase_root"
-   # this is now equivalent to ! -z "$upstream"
-   then
-   shortupstream=$(git rev-parse --short $upstream)
-   revisions=$upstream...$orig_head
-   shortrevisions=$shortupstream..$shorthead
-   else
-   revisions=$onto...$orig_head
-   shortrevisions=$shorthead
-   test -z "$squash_onto" ||
-   echo "$squash_onto" >"$state_dir"/squash-onto
-   fi
-}
-
 git_rebase__interactive () {
initiate_action "$action"
ret=$?
@@ -87,8 +70,6 @@ git_rebase__interactive () {
git rebase--helper --prepar

[GSoC][PATCH v8 15/20] rebase -i: rewrite write_basic_state() in C

2018-09-27 Thread Alban Gruin
This rewrites write_basic_state() from git-rebase.sh in C.  This is the
first step in the conversion of init_basic_state(), hence the mode in
rebase--helper.c is called INIT_BASIC_STATE.  init_basic_state() will be
converted in the next commit.

The part of read_strategy_opts() that parses the stategy options is
moved to a new function to allow its use in rebase--helper.c.

Finally, the call to write_basic_state() is removed from
git-rebase--interactive.sh, replaced by a call to `--init-basic-state`.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   | 28 +-
 git-rebase--interactive.sh |  7 +++-
 sequencer.c| 77 --
 sequencer.h|  4 ++
 4 files changed, 102 insertions(+), 14 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 0716bbfd78..63c5086e42 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -5,6 +5,8 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 #include "argv-array.h"
+#include "rerere.h"
+#include "alias.h"
 
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 
@@ -53,11 +55,12 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
int abbreviate_commands = 0, rebase_cousins = -1, ret;
const char *head_hash = NULL, *onto = NULL, *restrict_revision = NULL,
-   *squash_onto = NULL, *upstream = NULL;
+   *squash_onto = NULL, *upstream = NULL, *head_name = NULL;
+   char *raw_strategies = NULL;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC, EDIT_TODO, 
PREPARE_BRANCH,
-   COMPLETE_ACTION
+   COMPLETE_ACTION, INIT_BASIC_STATE
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -69,6 +72,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
 N_("keep original branch points of cousins")),
OPT_BOOL(0, "autosquash", ,
 N_("move commits that begin with squash!/fixup!")),
+   OPT_BOOL(0, "signoff", , N_("sign commits")),
OPT__VERBOSE(, N_("be verbose")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
@@ -93,6 +97,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
OPT_CMDMODE(0, "complete-action", ,
N_("complete the action"), COMPLETE_ACTION),
+   OPT_CMDMODE(0, "init-basic-state", ,
+   N_("initialise the rebase state"), 
INIT_BASIC_STATE),
OPT_STRING(0, "onto", , N_("onto"), N_("onto")),
OPT_STRING(0, "restrict-revision", _revision,
   N_("restrict-revision"), N_("restrict revision")),
@@ -100,6 +106,14 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
   N_("squash onto")),
OPT_STRING(0, "upstream", , N_("upstream"),
   N_("the upstream commit")),
+   OPT_STRING(0, "head-name", _name, N_("head-name"), 
N_("head name")),
+   OPT_STRING('S', "gpg-sign", _sign, N_("gpg-sign"),
+  N_("GPG-sign commits")),
+   OPT_STRING(0, "strategy", , N_("strategy"),
+  N_("rebase strategy")),
+   OPT_STRING(0, "strategy-opts", _strategies, 
N_("strategy-opts"),
+  N_("strategy options")),
+   OPT_RERERE_AUTOUPDATE(_rerere_auto),
OPT_END()
};
 
@@ -176,6 +190,16 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
free(shortrevisions);
return !!ret;
}
+   if (command == INIT_BASIC_STATE) {
+   if (raw_strategies)
+   parse_strategy_opts(, raw_strategies);
+
+   ret = get_revision_ranges(upstream, onto, _hash, NULL, 
NULL);
+   if (ret)
+   return ret;
+
+   return !!write_basic_state(, head_name, onto, head_hash);
+   }
 
usage_with_opti

[GSoC][PATCH v8 17/20] rebase -i: implement the main part of interactive rebase as a builtin

2018-09-27 Thread Alban Gruin
This rewrites the part of interactive rebase which initializes the
basic state, make the script and complete the action, as a buitin, named
git-rebase--interactive2 for now.  Others modes (`--continue`,
`--edit-todo`, etc.) will be rewritten in the next commit.

git-rebase--interactive.sh is modified to call git-rebase--interactive2
instead of git-rebase--helper.

Signed-off-by: Alban Gruin 
---
 .gitignore |   1 +
 Makefile   |   1 +
 builtin.h  |   1 +
 builtin/rebase--interactive2.c | 207 +
 git-rebase--interactive.sh |  41 ---
 git.c  |   1 +
 6 files changed, 233 insertions(+), 19 deletions(-)
 create mode 100644 builtin/rebase--interactive2.c

diff --git a/.gitignore b/.gitignore
index 9d1363a1eb..e33c09d52a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,6 +120,7 @@
 /git-rebase--am
 /git-rebase--helper
 /git-rebase--interactive
+/git-rebase--interactive2
 /git-rebase--merge
 /git-rebase--preserve-merges
 /git-receive-pack
diff --git a/Makefile b/Makefile
index ff7f01634f..bd10c87075 100644
--- a/Makefile
+++ b/Makefile
@@ -1082,6 +1082,7 @@ BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/range-diff.o
 BUILTIN_OBJS += builtin/read-tree.o
+BUILTIN_OBJS += builtin/rebase--interactive2.o
 BUILTIN_OBJS += builtin/rebase--helper.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index 962f0489ab..b79265d3d4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -204,6 +204,7 @@ extern int cmd_pull(int argc, const char **argv, const char 
*prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_range_diff(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase__interactive(int argc, const char **argv, const char 
*prefix);
 extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
diff --git a/builtin/rebase--interactive2.c b/builtin/rebase--interactive2.c
new file mode 100644
index 00..45336073a8
--- /dev/null
+++ b/builtin/rebase--interactive2.c
@@ -0,0 +1,207 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "parse-options.h"
+#include "sequencer.h"
+#include "rebase-interactive.h"
+#include "argv-array.h"
+#include "refs.h"
+#include "rerere.h"
+#include "run-command.h"
+
+static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
+
+static int get_revision_ranges(const char *upstream, const char *onto,
+  const char **head_hash,
+  char **revisions, char **shortrevisions)
+{
+   const char *base_rev = upstream ? upstream : onto, *shorthead;
+   struct object_id orig_head;
+
+   if (get_oid("HEAD", _head))
+   return error(_("no HEAD?"));
+
+   *head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
+   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+
+   shorthead = find_unique_abbrev(_head, DEFAULT_ABBREV);
+
+   if (upstream) {
+   const char *shortrev;
+   struct object_id rev_oid;
+
+   get_oid(base_rev, _oid);
+   shortrev = find_unique_abbrev(_oid, DEFAULT_ABBREV);
+
+   *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
+   } else
+   *shortrevisions = xstrdup(shorthead);
+
+   return 0;
+}
+
+static int init_basic_state(struct replay_opts *opts, const char *head_name,
+   const char *onto, const char *orig_head)
+{
+   FILE *interactive;
+
+   if (!is_directory(path_state_dir()) && 
mkdir_in_gitdir(path_state_dir()))
+   return error_errno(_("could not create temporary %s"), 
path_state_dir());
+
+   delete_reflog("REBASE_HEAD");
+
+   interactive = fopen(path_interactive(), "w");
+   if (!interactive)
+   return error_errno(_("could not mark as interactive"));
+   fclose(interactive);
+
+   return write_basic_state(opts, head_name, onto, orig_head);
+}
+
+static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
+const char *switch_to, const char *upstream,
+const char *onto, const char *onto_name,
+const char *squash_onto, c

[GSoC][PATCH v8 16/20] rebase -i: rewrite init_basic_state() in C

2018-09-27 Thread Alban Gruin
This rewrites init_basic_state() from shell to C.  The call to
write_basic_state() in cmd_rebase__helper() is replaced by a call to the
new function.

The shell version is then stripped from git-rebase--interactive.sh.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   | 23 ++-
 git-rebase--interactive.sh |  9 -
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 63c5086e42..f8128037d3 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -5,10 +5,13 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 #include "argv-array.h"
+#include "refs.h"
 #include "rerere.h"
 #include "alias.h"
 
+static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
@@ -44,6 +47,24 @@ static int get_revision_ranges(const char *upstream, const 
char *onto,
return 0;
 }
 
+static int init_basic_state(struct replay_opts *opts, const char *head_name,
+   const char *onto, const char *orig_head)
+{
+   FILE *interactive;
+
+   if (!is_directory(path_state_dir()) && 
mkdir_in_gitdir(path_state_dir()))
+   return error_errno(_("could not create temporary %s"), 
path_state_dir());
+
+   delete_reflog("REBASE_HEAD");
+
+   interactive = fopen(path_interactive(), "w");
+   if (!interactive)
+   return error_errno(_("could not mark as interactive"));
+   fclose(interactive);
+
+   return write_basic_state(opts, head_name, onto, orig_head);
+}
+
 static const char * const builtin_rebase_helper_usage[] = {
N_("git rebase--helper []"),
NULL
@@ -198,7 +219,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (ret)
return ret;
 
-   return !!write_basic_state(, head_name, onto, head_hash);
+   return !!init_basic_state(, head_name, onto, head_hash);
}
 
usage_with_options(builtin_rebase_helper_usage, options);
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 6367da66e2..761be95ed1 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -51,14 +51,6 @@ initiate_action () {
esac
 }
 
-init_basic_state () {
-   orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
-   mkdir -p "$state_dir" || die "$(eval_gettext "Could not create 
temporary \$state_dir")"
-   rm -f "$(git rev-parse --git-path REBASE_HEAD)"
-
-   : > "$state_dir"/interactive || die "$(gettext "Could not mark as 
interactive")"
-}
-
 git_rebase__interactive () {
initiate_action "$action"
ret=$?
@@ -67,7 +59,6 @@ git_rebase__interactive () {
fi
 
git rebase--helper --prepare-branch "$switch_to" ${verbose:+--verbose}
-   init_basic_state
 
git rebase--helper --init-basic-state ${upstream:+--upstream 
"$upstream"} \
${onto:+--onto "$onto"} ${head_name:+--head-name "$head_name"} \
-- 
2.19.0



[GSoC][PATCH v8 20/20] rebase -i: move rebase--helper modes to rebase--interactive

2018-09-27 Thread Alban Gruin
This moves the rebase--helper modes still used by
git-rebase--preserve-merges.sh (`--shorten-ids`, `--expand-ids`,
`--check-todo-list`, `--rearrange-squash` and `--add-exec-commands`) to
rebase--interactive.c.

git-rebase--preserve-merges.sh is modified accordingly, and
rebase--helper.c is removed as it is useless now.

Signed-off-by: Alban Gruin 
---
The changes are due to the rebase, no real changes otherwise.

 .gitignore |   1 -
 Makefile   |   1 -
 builtin.h  |   1 -
 builtin/rebase--helper.c   | 226 -
 builtin/rebase--interactive.c  |  27 +++-
 git-rebase--preserve-merges.sh |  10 +-
 git.c  |   1 -
 7 files changed, 31 insertions(+), 236 deletions(-)
 delete mode 100644 builtin/rebase--helper.c

diff --git a/.gitignore b/.gitignore
index 9d1363a1eb..1c0394f576 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,7 +118,6 @@
 /git-read-tree
 /git-rebase
 /git-rebase--am
-/git-rebase--helper
 /git-rebase--interactive
 /git-rebase--merge
 /git-rebase--preserve-merges
diff --git a/Makefile b/Makefile
index dc26276747..ec4ee78b3f 100644
--- a/Makefile
+++ b/Makefile
@@ -1081,7 +1081,6 @@ BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/range-diff.o
 BUILTIN_OBJS += builtin/read-tree.o
-BUILTIN_OBJS += builtin/rebase--helper.o
 BUILTIN_OBJS += builtin/rebase--interactive.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index b79265d3d4..f6a7d82d73 100644
--- a/builtin.h
+++ b/builtin.h
@@ -205,7 +205,6 @@ extern int cmd_push(int argc, const char **argv, const char 
*prefix);
 extern int cmd_range_diff(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_rebase__interactive(int argc, const char **argv, const char 
*prefix);
-extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
deleted file mode 100644
index f8128037d3..00
--- a/builtin/rebase--helper.c
+++ /dev/null
@@ -1,226 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "config.h"
-#include "parse-options.h"
-#include "sequencer.h"
-#include "rebase-interactive.h"
-#include "argv-array.h"
-#include "refs.h"
-#include "rerere.h"
-#include "alias.h"
-
-static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
-static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
-static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
-
-static int get_revision_ranges(const char *upstream, const char *onto,
-  const char **head_hash,
-  char **revisions, char **shortrevisions)
-{
-   const char *base_rev = upstream ? upstream : onto;
-   struct object_id orig_head;
-
-   if (get_oid("HEAD", _head))
-   return error(_("no HEAD?"));
-
-   *head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
-
-   if (revisions)
-   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
-   if (shortrevisions) {
-   const char *shorthead;
-
-   shorthead = find_unique_abbrev(_head, DEFAULT_ABBREV);
-
-   if (upstream) {
-   const char *shortrev;
-   struct object_id rev_oid;
-
-   get_oid(base_rev, _oid);
-   shortrev = find_unique_abbrev(_oid, DEFAULT_ABBREV);
-
-   *shortrevisions = xstrfmt("%s..%s", shortrev, 
shorthead);
-   } else
-   *shortrevisions = xstrdup(shorthead);
-   }
-
-   return 0;
-}
-
-static int init_basic_state(struct replay_opts *opts, const char *head_name,
-   const char *onto, const char *orig_head)
-{
-   FILE *interactive;
-
-   if (!is_directory(path_state_dir()) && 
mkdir_in_gitdir(path_state_dir()))
-   return error_errno(_("could not create temporary %s"), 
path_state_dir());
-
-   delete_reflog("REBASE_HEAD");
-
-   interactive = fopen(path_interactive(), "w");
-   if (!interactive)
-   return error_errno(_("could not mark as interactive"));
-   fclose(interactive);
-
-   return write_basic_state(opts, head_name, onto, orig_head);
-}
-
-static const char * const builtin_rebase_helper_usage[] = {
-   N_("git rebase--helper []"

[GSoC][PATCH v8 19/20] rebase -i: remove git-rebase--interactive.sh

2018-09-27 Thread Alban Gruin
This removes git-rebase--interactive.sh, as its functionnality has been
replaced by git-rebase--interactive2.

git-rebase--interactive2.c is then renamed to git-rebase--interactive.c.

Signed-off-by: Alban Gruin 
---
The changes are due to the rebase, no real changes otherwise.

 .gitignore|  1 -
 Makefile  |  4 +-
 ...--interactive2.c => rebase--interactive.c} |  0
 git-rebase--interactive.sh| 84 ---
 git-rebase.sh |  2 +-
 git.c |  2 +-
 6 files changed, 3 insertions(+), 90 deletions(-)
 rename builtin/{rebase--interactive2.c => rebase--interactive.c} (100%)
 delete mode 100644 git-rebase--interactive.sh

diff --git a/.gitignore b/.gitignore
index e33c09d52a..9d1363a1eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,7 +120,6 @@
 /git-rebase--am
 /git-rebase--helper
 /git-rebase--interactive
-/git-rebase--interactive2
 /git-rebase--merge
 /git-rebase--preserve-merges
 /git-receive-pack
diff --git a/Makefile b/Makefile
index bd10c87075..dc26276747 100644
--- a/Makefile
+++ b/Makefile
@@ -624,7 +624,6 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--am
-SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
@@ -1082,8 +1081,8 @@ BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/range-diff.o
 BUILTIN_OBJS += builtin/read-tree.o
-BUILTIN_OBJS += builtin/rebase--interactive2.o
 BUILTIN_OBJS += builtin/rebase--helper.o
+BUILTIN_OBJS += builtin/rebase--interactive.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
 BUILTIN_OBJS += builtin/remote.o
@@ -2422,7 +2421,6 @@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
-LOCALIZED_SH += git-rebase--interactive.sh
 LOCALIZED_SH += git-rebase--preserve-merges.sh
 LOCALIZED_SH += git-sh-setup.sh
 LOCALIZED_PERL = $(SCRIPT_PERL)
diff --git a/builtin/rebase--interactive2.c b/builtin/rebase--interactive.c
similarity index 100%
rename from builtin/rebase--interactive2.c
rename to builtin/rebase--interactive.c
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
deleted file mode 100644
index e87d708a4d..00
--- a/git-rebase--interactive.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-# This shell script fragment is sourced by git-rebase to implement
-# its interactive mode.  "git rebase --interactive" makes it easy
-# to fix up commits in the middle of a series and rearrange commits.
-#
-# Copyright (c) 2006 Johannes E. Schindelin
-#
-# The original idea comes from Eric W. Biederman, in
-# https://public-inbox.org/git/m1odwkyuf5.fsf...@ebiederm.dsl.xmission.com/
-#
-# The file containing rebase commands, comments, and empty lines.
-# This file is created by "git rebase -i" then edited by the user.  As
-# the lines are processed, they are removed from the front of this
-# file and written to the tail of $done.
-todo="$state_dir"/git-rebase-todo
-
-GIT_CHERRY_PICK_HELP="$resolvemsg"
-export GIT_CHERRY_PICK_HELP
-
-# Initiate an action. If the cannot be any
-# further action it  may exec a command
-# or exit and not return.
-#
-# TODO: Consider a cleaner return model so it
-# never exits and always return 0 if process
-# is complete.
-#
-# Parameter 1 is the action to initiate.
-#
-# Returns 0 if the action was able to complete
-# and if 1 if further processing is required.
-initiate_action () {
-   case "$1" in
-   continue)
-   exec git rebase--helper ${force_rebase:+--no-ff} 
$allow_empty_message \
---continue
-   ;;
-   skip)
-   git rerere clear
-   exec git rebase--helper ${force_rebase:+--no-ff} 
$allow_empty_message \
---continue
-   ;;
-   edit-todo)
-   exec git rebase--helper --edit-todo
-   ;;
-   show-current-patch)
-   exec git show REBASE_HEAD --
-   ;;
-   *)
-   return 1 # continue
-   ;;
-   esac
-}
-
-git_rebase__interactive () {
-   initiate_action "$action"
-   ret=$?
-   if test $ret = 0; then
-   return 0
-   fi
-
-   test -n "$keep_empty" && keep_empty="--keep-empty"
-   test -n "$rebase_merges" && rebase_merges="--rebase-merges"
-   test -n "$rebase_cousins" && rebase_cousins="--rebase-cousins"
-   test -n "$autosquash" && autosquash="--autosquash"
-   test -n "$verbose" && ve

[GSoC][PATCH v8 12/20] rebase -i: remove unused modes and functions

2018-09-27 Thread Alban Gruin
This removes the modes `--skip-unnecessary-picks`, `--append-todo-help`,
and `--checkout-onto` from rebase--helper.c, the functions of
git-rebase--interactive.sh that were rendered useless by the rewrite of
complete_action(), and append_todo_help_to_file() from
rebase-interactive.c.

skip_unnecessary_picks() and checkout_onto() becomes static, as they are
only used inside of the sequencer.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   | 23 ++
 git-rebase--interactive.sh | 50 --
 rebase-interactive.c   | 22 -
 rebase-interactive.h   |  1 -
 sequencer.c|  8 +++---
 sequencer.h|  4 ---
 6 files changed, 6 insertions(+), 102 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 01b958..e1460136f5 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -17,9 +17,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
int abbreviate_commands = 0, rebase_cousins = -1;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
-   CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH,
-   CHECKOUT_ONTO, COMPLETE_ACTION
+   CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC, EDIT_TODO, 
PREPARE_BRANCH,
+   COMPLETE_ACTION
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -44,21 +43,15 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("expand commit ids in the todo list"), EXPAND_OIDS),
OPT_CMDMODE(0, "check-todo-list", ,
N_("check the todo list"), CHECK_TODO_LIST),
-   OPT_CMDMODE(0, "skip-unnecessary-picks", ,
-   N_("skip unnecessary picks"), SKIP_UNNECESSARY_PICKS),
OPT_CMDMODE(0, "rearrange-squash", ,
N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
OPT_CMDMODE(0, "add-exec-commands", ,
N_("insert exec commands in todo list"), ADD_EXEC),
-   OPT_CMDMODE(0, "append-todo-help", ,
-   N_("insert the help in the todo list"), 
APPEND_TODO_HELP),
OPT_CMDMODE(0, "edit-todo", ,
N_("edit the todo list during an interactive 
rebase"),
EDIT_TODO),
OPT_CMDMODE(0, "prepare-branch", ,
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
-   OPT_CMDMODE(0, "checkout-onto", ,
-   N_("checkout a commit"), CHECKOUT_ONTO),
OPT_CMDMODE(0, "complete-action", ,
N_("complete the action"), COMPLETE_ACTION),
OPT_END()
@@ -94,26 +87,14 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!transform_todos(flags);
if (command == CHECK_TODO_LIST && argc == 1)
return !!check_todo_list();
-   if (command == SKIP_UNNECESSARY_PICKS && argc == 1) {
-   struct object_id oid;
-   int ret = skip_unnecessary_picks();
-
-   if (!ret)
-   puts(oid_to_hex());
-   return !!ret;
-   }
if (command == REARRANGE_SQUASH && argc == 1)
return !!rearrange_squash();
if (command == ADD_EXEC && argc == 2)
return !!sequencer_add_exec_commands(argv[1]);
-   if (command == APPEND_TODO_HELP && argc == 1)
-   return !!append_todo_help_to_file(0, keep_empty);
if (command == EDIT_TODO && argc == 1)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
return !!prepare_branch_to_be_rebased(, argv[1]);
-   if (command == CHECKOUT_ONTO && argc == 4)
-   return !!checkout_onto(, argv[1], argv[2], argv[3]);
if (command == COMPLETE_ACTION && argc == 6)
return !!complete_action(, flags, argv[1], argv[2], 
argv[3],
 argv[4], argv[5], autosquash);
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 59dc4888a6..0d66c0f8b8 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -16,56 +16,6 @@ todo="$state_dir"/git-rebase-todo
 GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_

[GSoC][PATCH v8 07/20] rebase -i: rewrite checkout_onto() in C

2018-09-27 Thread Alban Gruin
This rewrites checkout_onto() from shell to C.

A new command (“checkout-onto”) is added to rebase--helper.c. The shell
version is then stripped.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   |  7 ++-
 git-rebase--interactive.sh | 25 -
 sequencer.c| 19 +++
 sequencer.h|  3 +++
 4 files changed, 32 insertions(+), 22 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 0e76dadba6..7d9426d23c 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -18,7 +18,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH
+   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH,
+   CHECKOUT_ONTO
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -54,6 +55,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
EDIT_TODO),
OPT_CMDMODE(0, "prepare-branch", ,
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
+   OPT_CMDMODE(0, "checkout-onto", ,
+   N_("checkout a commit"), CHECKOUT_ONTO),
OPT_END()
};
 
@@ -99,5 +102,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
return !!prepare_branch_to_be_rebased(, argv[1]);
+   if (command == CHECKOUT_ONTO && argc == 4)
+   return !!checkout_onto(, argv[1], argv[2], argv[3]);
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 77e972bb6c..b68f108f28 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -28,17 +28,6 @@ case "$comment_char" in
;;
 esac
 
-orig_reflog_action="$GIT_REFLOG_ACTION"
-
-comment_for_reflog () {
-   case "$orig_reflog_action" in
-   ''|rebase*)
-   GIT_REFLOG_ACTION="rebase -i ($1)"
-   export GIT_REFLOG_ACTION
-   ;;
-   esac
-}
-
 die_abort () {
apply_autostash
rm -rf "$state_dir"
@@ -70,14 +59,6 @@ collapse_todo_ids() {
git rebase--helper --shorten-ids
 }
 
-# Switch to the branch in $into and notify it in the reflog
-checkout_onto () {
-   comment_for_reflog start
-   GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
-   output git checkout $onto || die_abort "$(gettext "could not detach 
HEAD")"
-   git update-ref ORIG_HEAD $orig_head
-}
-
 get_missing_commit_check_level () {
check_level=$(git config --get rebase.missingCommitsCheck)
check_level=${check_level:-ignore}
@@ -176,7 +157,8 @@ EOF
 
git rebase--helper --check-todo-list || {
ret=$?
-   checkout_onto
+   git rebase--helper --checkout-onto "$onto_name" "$onto" \
+   "$orig_head" ${verbose:+--verbose}
exit $ret
}
 
@@ -186,7 +168,8 @@ EOF
onto="$(git rebase--helper --skip-unnecessary-picks)" ||
die "Could not skip unnecessary pick commands"
 
-   checkout_onto
+   git rebase--helper --checkout-onto "$onto_name" "$onto" "$orig_head" \
+   ${verbose:+--verbose}
require_clean_work_tree "rebase"
exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
 --continue
diff --git a/sequencer.c b/sequencer.c
index d8a59c50f8..c7f930ab9c 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3315,6 +3315,25 @@ int prepare_branch_to_be_rebased(struct replay_opts 
*opts, const char *commit)
return 0;
 }
 
+int checkout_onto(struct replay_opts *opts,
+ const char *onto_name, const char *onto,
+ const char *orig_head)
+{
+   struct object_id oid;
+   const char *action = reflog_message(opts, "start", "checkout %s", 
onto_name);
+
+   if (get_oid(orig_head, ))
+   return error(_("%s: not a valid OID"), orig_head);
+
+   if (run_git_checkout(opts, onto, action)) {
+   apply_autostash(opts);
+   sequencer_remove_state(opts);
+   return error(_("could not detach HEAD"));
+   }
+
+   return update_ref(N

[GSoC][PATCH v8 04/20] rebase -i: rewrite the edit-todo functionality in C

2018-09-27 Thread Alban Gruin
This rewrites the edit-todo functionality from shell to C.

To achieve that, a new command mode, `edit-todo`, is added, and the
`write-edit-todo` flag is removed, as the shell script does not need to
write the edit todo help message to the todo list anymore.

The shell version is then stripped in favour of a call to the helper.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   | 13 -
 git-rebase--interactive.sh | 11 +--
 rebase-interactive.c   | 27 +++
 rebase-interactive.h   |  1 +
 4 files changed, 37 insertions(+), 15 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 05e73e71d4..731a64971d 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -13,12 +13,12 @@ static const char * const builtin_rebase_helper_usage[] = {
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
-   unsigned flags = 0, keep_empty = 0, rebase_merges = 0, write_edit_todo 
= 0;
+   unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
int abbreviate_commands = 0, rebase_cousins = -1;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC, APPEND_TODO_HELP
+   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -28,8 +28,6 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
OPT_BOOL(0, "rebase-merges", _merges, N_("rebase merge 
commits")),
OPT_BOOL(0, "rebase-cousins", _cousins,
 N_("keep original branch points of cousins")),
-   OPT_BOOL(0, "write-edit-todo", _edit_todo,
-N_("append the edit-todo message to the todo-list")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
OPT_CMDMODE(0, "abort", , N_("abort rebase"),
@@ -50,6 +48,9 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("insert exec commands in todo list"), ADD_EXEC),
OPT_CMDMODE(0, "append-todo-help", ,
N_("insert the help in the todo list"), 
APPEND_TODO_HELP),
+   OPT_CMDMODE(0, "edit-todo", ,
+   N_("edit the todo list during an interactive 
rebase"),
+   EDIT_TODO),
OPT_END()
};
 
@@ -90,6 +91,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (command == ADD_EXEC && argc == 2)
return !!sequencer_add_exec_commands(argv[1]);
if (command == APPEND_TODO_HELP && argc == 1)
-   return !!append_todo_help(write_edit_todo, keep_empty);
+   return !!append_todo_help(0, keep_empty);
+   if (command == EDIT_TODO && argc == 1)
+   return !!edit_todo_list(flags);
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 94c23a7af2..2defe607f4 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -108,16 +108,7 @@ initiate_action () {
 --continue
;;
edit-todo)
-   git stripspace --strip-comments <"$todo" >"$todo".new
-   mv -f "$todo".new "$todo"
-   collapse_todo_ids
-   git rebase--helper --append-todo-help --write-edit-todo
-
-   git_sequence_editor "$todo" ||
-   die "$(gettext "Could not execute editor")"
-   expand_todo_ids
-
-   exit
+   exec git rebase--helper --edit-todo
;;
show-current-patch)
exec git show REBASE_HEAD --
diff --git a/rebase-interactive.c b/rebase-interactive.c
index d7996bc8d9..3f9468fc69 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -66,3 +66,30 @@ int append_todo_help(unsigned edit_todo, unsigned keep_empty)
 
return ret;
 }
+
+int edit_todo_list(unsigned flags)
+{
+   struct strbuf buf = STRBUF_INIT;
+   const char *todo_file = rebase_path_todo();
+
+   if (strbuf_read_file(, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   strbuf_stripspace(, 1);
+   if (write_message(buf.buf, buf.len, todo_file, 0)) {
+   str

[GSoC][PATCH v8 06/20] rebase -i: rewrite setup_reflog_action() in C

2018-09-27 Thread Alban Gruin
This rewrites (the misnamed) setup_reflog_action() from shell to C. The
new version is called prepare_branch_to_be_rebased().

A new command is added to rebase--helper.c, “checkout-base”, as well as
a new flag, “verbose”, to avoid silencing the output of the checkout
operation called by checkout_base_commit().

The function `run_git_checkout()` will also be used in the next commit,
therefore its code is not part of `checkout_base_commit()`.

The shell version is then stripped in favour of a call to the helper.

As $GIT_REFLOG_ACTION is no longer set at the first call of
checkout_onto(), a call to comment_for_reflog() is added at the
beginning of this function.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c   |  7 ++-
 git-rebase--interactive.sh | 16 ++--
 sequencer.c| 30 ++
 sequencer.h|  2 ++
 4 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 731a64971d..0e76dadba6 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -18,7 +18,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO
+   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -28,6 +28,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
OPT_BOOL(0, "rebase-merges", _merges, N_("rebase merge 
commits")),
OPT_BOOL(0, "rebase-cousins", _cousins,
 N_("keep original branch points of cousins")),
+   OPT__VERBOSE(, N_("be verbose")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
OPT_CMDMODE(0, "abort", , N_("abort rebase"),
@@ -51,6 +52,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
OPT_CMDMODE(0, "edit-todo", ,
N_("edit the todo list during an interactive 
rebase"),
EDIT_TODO),
+   OPT_CMDMODE(0, "prepare-branch", ,
+   N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
OPT_END()
};
 
@@ -94,5 +97,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!append_todo_help(0, keep_empty);
if (command == EDIT_TODO && argc == 1)
return !!edit_todo_list(flags);
+   if (command == PREPARE_BRANCH && argc == 2)
+   return !!prepare_branch_to_be_rebased(, argv[1]);
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 2defe607f4..77e972bb6c 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -72,6 +72,7 @@ collapse_todo_ids() {
 
 # Switch to the branch in $into and notify it in the reflog
 checkout_onto () {
+   comment_for_reflog start
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
output git checkout $onto || die_abort "$(gettext "could not detach 
HEAD")"
git update-ref ORIG_HEAD $orig_head
@@ -119,19 +120,6 @@ initiate_action () {
esac
 }
 
-setup_reflog_action () {
-   comment_for_reflog start
-
-   if test ! -z "$switch_to"
-   then
-   GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
-   output git checkout "$switch_to" -- ||
-   die "$(eval_gettext "Could not checkout \$switch_to")"
-
-   comment_for_reflog start
-   fi
-}
-
 init_basic_state () {
orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
mkdir -p "$state_dir" || die "$(eval_gettext "Could not create 
temporary \$state_dir")"
@@ -211,7 +199,7 @@ git_rebase__interactive () {
return 0
fi
 
-   setup_reflog_action
+   git rebase--helper --prepare-branch "$switch_to" ${verbose:+--verbose}
init_basic_state
 
init_revisions_and_shortrevisions
diff --git a/sequencer.c b/sequencer.c
index 5919ad312e..d8a59c50f8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3285,6 +3285,36 @@ static const char *reflog_message(struct replay_opts 
*opts,
return buf.buf;
 }
 
+static i

[GSoC][PATCH v8 02/20] rebase -i: rewrite append_todo_help() in C

2018-09-27 Thread Alban Gruin
This rewrites append_todo_help() from shell to C. It also incorporates
some parts of initiate_action() and complete_action() that also write
help texts to the todo file.

This also introduces the source file rebase-interactive.c. This file
will contain functions necessary for interactive rebase that are too
specific for the sequencer, and is part of libgit.a.

Two flags are added to rebase--helper.c: one to call
append_todo_help() (`--append-todo-help`), and another one to tell
append_todo_help() to write the help text suited for the edit-todo
mode (`--write-edit-todo`).

Finally, append_todo_help() is removed from git-rebase--interactive.sh
to use `rebase--helper --append-todo-help` instead.

Signed-off-by: Alban Gruin 
---
The changes are due to the rebase, no real changes otherwise.

 Makefile   |  1 +
 builtin/rebase--helper.c   | 11 --
 git-rebase--interactive.sh | 52 ++---
 rebase-interactive.c   | 68 ++
 rebase-interactive.h   |  6 
 5 files changed, 86 insertions(+), 52 deletions(-)
 create mode 100644 rebase-interactive.c
 create mode 100644 rebase-interactive.h

diff --git a/Makefile b/Makefile
index 13e1c52478..ff7f01634f 100644
--- a/Makefile
+++ b/Makefile
@@ -942,6 +942,7 @@ LIB_OBJS += quote.o
 LIB_OBJS += range-diff.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
+LIB_OBJS += rebase-interactive.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index f7c2a5fdc8..05e73e71d4 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -3,6 +3,7 @@
 #include "config.h"
 #include "parse-options.h"
 #include "sequencer.h"
+#include "rebase-interactive.h"
 
 static const char * const builtin_rebase_helper_usage[] = {
N_("git rebase--helper []"),
@@ -12,12 +13,12 @@ static const char * const builtin_rebase_helper_usage[] = {
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
-   unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
+   unsigned flags = 0, keep_empty = 0, rebase_merges = 0, write_edit_todo 
= 0;
int abbreviate_commands = 0, rebase_cousins = -1;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC
+   ADD_EXEC, APPEND_TODO_HELP
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -27,6 +28,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
OPT_BOOL(0, "rebase-merges", _merges, N_("rebase merge 
commits")),
OPT_BOOL(0, "rebase-cousins", _cousins,
 N_("keep original branch points of cousins")),
+   OPT_BOOL(0, "write-edit-todo", _edit_todo,
+N_("append the edit-todo message to the todo-list")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
OPT_CMDMODE(0, "abort", , N_("abort rebase"),
@@ -45,6 +48,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
OPT_CMDMODE(0, "add-exec-commands", ,
N_("insert exec commands in todo list"), ADD_EXEC),
+   OPT_CMDMODE(0, "append-todo-help", ,
+   N_("insert the help in the todo list"), 
APPEND_TODO_HELP),
OPT_END()
};
 
@@ -84,5 +89,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!rearrange_squash();
if (command == ADD_EXEC && argc == 2)
return !!sequencer_add_exec_commands(argv[1]);
+   if (command == APPEND_TODO_HELP && argc == 1)
+   return !!append_todo_help(write_edit_todo, keep_empty);
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 299ded2137..94c23a7af2 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -39,38 +39,6 @@ comment_for_reflog () {
esac
 }
 
-append_todo_help () {
-   gettext "
-Commands:
-p, pick  = use commit
-r, reword  = use commit, but edit the commit message
-e, edit  = use commit, but stop for amending
-s, squash  = use commit, but meld into previous commit
-f, fixup  = like \"squash\", but discard this commit's log message
-x, e

[GSoC][PATCH v8 08/20] sequencer: refactor append_todo_help() to write its message to a buffer

2018-09-27 Thread Alban Gruin
This refactors append_todo_help() to write its message to a buffer
instead of the todo-list.  This is needed for the rewrite of
complete_action(), which will come after the next commit.

As rebase--helper still needs the file manipulation part of
append_todo_help(), it is extracted to a temporary function,
append_todo_help_to_file().  This function will go away after the
rewrite of complete_action().

Signed-off-by: Alban Gruin 
---
No changes since v7.

 builtin/rebase--helper.c |  2 +-
 rebase-interactive.c | 43 
 rebase-interactive.h |  4 +++-
 3 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 7d9426d23c..313092c465 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -97,7 +97,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (command == ADD_EXEC && argc == 2)
return !!sequencer_add_exec_commands(argv[1]);
if (command == APPEND_TODO_HELP && argc == 1)
-   return !!append_todo_help(0, keep_empty);
+   return !!append_todo_help_to_file(0, keep_empty);
if (command == EDIT_TODO && argc == 1)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 3f9468fc69..4a9a10eff4 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -4,11 +4,9 @@
 #include "sequencer.h"
 #include "strbuf.h"
 
-int append_todo_help(unsigned edit_todo, unsigned keep_empty)
+void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+ struct strbuf *buf)
 {
-   struct strbuf buf = STRBUF_INIT;
-   FILE *todo;
-   int ret;
const char *msg = _("\nCommands:\n"
 "p, pick  = use commit\n"
 "r, reword  = use commit, but edit the commit message\n"
@@ -26,11 +24,7 @@ int append_todo_help(unsigned edit_todo, unsigned keep_empty)
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
 
-   todo = fopen_or_warn(rebase_path_todo(), "a");
-   if (!todo)
-   return 1;
-
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
 
if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
msg = _("\nDo not remove any line. Use 'drop' "
@@ -39,7 +33,7 @@ int append_todo_help(unsigned edit_todo, unsigned keep_empty)
msg = _("\nIf you remove a line here "
 "THAT COMMIT WILL BE LOST.\n");
 
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
 
if (edit_todo)
msg = _("\nYou are editing the todo file "
@@ -50,12 +44,25 @@ int append_todo_help(unsigned edit_todo, unsigned 
keep_empty)
msg = _("\nHowever, if you remove everything, "
"the rebase will be aborted.\n\n");
 
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
 
if (!keep_empty) {
msg = _("Note that empty commits are commented out");
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
}
+}
+
+int append_todo_help_to_file(unsigned edit_todo, unsigned keep_empty)
+{
+   struct strbuf buf = STRBUF_INIT;
+   FILE *todo;
+   int ret;
+
+   todo = fopen_or_warn(rebase_path_todo(), "a");
+   if (!todo)
+   return -1;
+
+   append_todo_help(edit_todo, keep_empty, );
 
ret = fputs(buf.buf, todo);
if (ret < 0)
@@ -84,7 +91,17 @@ int edit_todo_list(unsigned flags)
strbuf_release();
 
transform_todos(flags | TODO_LIST_SHORTEN_IDS);
-   append_todo_help(1, 0);
+
+   if (strbuf_read_file(, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   append_todo_help(1, 0, );
+   if (write_message(buf.buf, buf.len, todo_file, 0)) {
+   strbuf_release();
+   return -1;
+   }
+
+   strbuf_release();
 
if (launch_sequence_editor(todo_file, NULL, NULL))
return -1;
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 155219e742..d33f3176b7 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -1,7 +1,9 @@
 #ifndef REBASE_INTERACTIVE_H
 #define REBASE_INTERACTIVE_H
 
-int append_todo_help(unsigned edit_todo, unsigned keep_empty);
+void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+ struct strbuf *buf);
+int append_todo_help_to_file(unsigned edit_todo, unsigned keep_empty);
 int edit_todo_list(unsigned flags);
 
 #endif
-- 
2.19.0



[GSoC][PATCH v8 03/20] editor: add a function to launch the sequence editor

2018-09-27 Thread Alban Gruin
As part of the rewrite of interactive rebase, the sequencer will need to
open the sequence editor to allow the user to edit the todo list.
Instead of duplicating the existing launch_editor() function, this
refactors it to a new function, launch_specified_editor(), which takes
the editor as a parameter, in addition to the path, the buffer and the
environment variables.  launch_sequence_editor() is then added to launch
the sequence editor.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 cache.h  |  1 +
 editor.c | 27 +--
 strbuf.h |  2 ++
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/cache.h b/cache.h
index d508f3d4f8..a976e26a02 100644
--- a/cache.h
+++ b/cache.h
@@ -1482,6 +1482,7 @@ extern const char *fmt_name(const char *name, const char 
*email);
 extern const char *ident_default_name(void);
 extern const char *ident_default_email(void);
 extern const char *git_editor(void);
+extern const char *git_sequence_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int is_terminal_dumb(void);
 extern int git_ident_config(const char *, const char *, void *);
diff --git a/editor.c b/editor.c
index 9a9b4e12d1..c985eee1f9 100644
--- a/editor.c
+++ b/editor.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "config.h"
 #include "strbuf.h"
 #include "run-command.h"
 #include "sigchain.h"
@@ -34,10 +35,21 @@ const char *git_editor(void)
return editor;
 }
 
-int launch_editor(const char *path, struct strbuf *buffer, const char *const 
*env)
+const char *git_sequence_editor(void)
 {
-   const char *editor = git_editor();
+   const char *editor = getenv("GIT_SEQUENCE_EDITOR");
+
+   if (!editor)
+   git_config_get_string_const("sequence.editor", );
+   if (!editor)
+   editor = git_editor();
 
+   return editor;
+}
+
+static int launch_specified_editor(const char *editor, const char *path,
+  struct strbuf *buffer, const char *const 
*env)
+{
if (!editor)
return error("Terminal is dumb, but EDITOR unset");
 
@@ -95,3 +107,14 @@ int launch_editor(const char *path, struct strbuf *buffer, 
const char *const *en
return error_errno("could not read file '%s'", path);
return 0;
 }
+
+int launch_editor(const char *path, struct strbuf *buffer, const char *const 
*env)
+{
+   return launch_specified_editor(git_editor(), path, buffer, env);
+}
+
+int launch_sequence_editor(const char *path, struct strbuf *buffer,
+  const char *const *env)
+{
+   return launch_specified_editor(git_sequence_editor(), path, buffer, 
env);
+}
diff --git a/strbuf.h b/strbuf.h
index 60a35aef16..66da9822fd 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -575,6 +575,8 @@ extern void strbuf_add_unique_abbrev(struct strbuf *sb,
  * 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);
+extern 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);
 
-- 
2.19.0



[GSoC][PATCH v8 05/20] sequencer: add a new function to silence a command, except if it fails

2018-09-27 Thread Alban Gruin
This adds a new function, run_command_silent_on_success(), to
redirect the stdout and stderr of a command to a strbuf, and then to run
that command. This strbuf is printed only if the command fails. It is
functionnaly similar to output() from git-rebase.sh.

run_git_commit() is then refactored to use of
run_command_silent_on_success().

Signed-off-by: Alban Gruin 
---
The changes are due to the rebase, no real changes otherwise.

 sequencer.c | 51 +--
 1 file changed, 25 insertions(+), 26 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index c24d37dfb1..5919ad312e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -804,6 +804,23 @@ N_("you have staged changes in your working tree\n"
 #define VERIFY_MSG  (1<<4)
 #define CREATE_ROOT_COMMIT (1<<5)
 
+static int run_command_silent_on_success(struct child_process *cmd)
+{
+   struct strbuf buf = STRBUF_INIT;
+   int rc;
+
+   cmd->stdout_to_stderr = 1;
+   rc = pipe_command(cmd,
+ NULL, 0,
+ NULL, 0,
+ , 0);
+
+   if (rc)
+   fputs(buf.buf, stderr);
+   strbuf_release();
+   return rc;
+}
+
 /*
  * If we are cherry-pick, and if the merge did not result in
  * hand-editing, we will hit this commit and inherit the original
@@ -865,18 +882,11 @@ static int run_git_commit(const char *defmsg, struct 
replay_opts *opts,
 
cmd.git_cmd = 1;
 
-   if (is_rebase_i(opts)) {
-   if (!(flags & EDIT_MSG)) {
-   cmd.stdout_to_stderr = 1;
-   cmd.err = -1;
-   }
+   if (is_rebase_i(opts) && read_env_script(_array)) {
+   const char *gpg_opt = gpg_sign_opt_quoted(opts);
 
-   if (read_env_script(_array)) {
-   const char *gpg_opt = gpg_sign_opt_quoted(opts);
-
-   return error(_(staged_changes_advice),
-gpg_opt, gpg_opt);
-   }
+   return error(_(staged_changes_advice),
+gpg_opt, gpg_opt);
}
 
argv_array_push(, "commit");
@@ -906,21 +916,10 @@ static int run_git_commit(const char *defmsg, struct 
replay_opts *opts,
if (!(flags & EDIT_MSG))
argv_array_push(, "--allow-empty-message");
 
-   if (cmd.err == -1) {
-   /* hide stderr on success */
-   struct strbuf buf = STRBUF_INIT;
-   int rc = pipe_command(,
- NULL, 0,
- /* stdout is already redirected */
- NULL, 0,
- , 0);
-   if (rc)
-   fputs(buf.buf, stderr);
-   strbuf_release();
-   return rc;
-   }
-
-   return run_command();
+   if (is_rebase_i(opts) && !(flags & EDIT_MSG))
+   return run_command_silent_on_success();
+   else
+   return run_command();
 }
 
 static int rest_is_empty(const struct strbuf *sb, int start)
-- 
2.19.0



[GSoC][PATCH v8 00/20] rebase -i: rewrite in C

2018-09-27 Thread Alban Gruin
This patch series rewrite the interactive rebase from shell to C.

The v7 was based on ffc6fa0e39 ("Fourth batch for 2.19 cycle",
2018-07-24), but this series is based on fe8321ec05 ("Second batch post
2.19", 2017-09-24) due to some conflicts.

Changes since v7:

 - [17/20] The optionnal parameter of `-S' was mandatory in
   rebase--builtin2, making it abort when a signing key is provided to
   `user.signingkey' and `commit.gpgsign' is set to true.  First
   reported on github[0], fixed by Johannes Schindelin.

 - [2, 5, 19, 20/20] Changes due to the rebase.

 - [9, 11/20] Conflict solving.

Alban Gruin (20):
  sequencer: make three functions and an enum from sequencer.c public
  rebase -i: rewrite append_todo_help() in C
  editor: add a function to launch the sequence editor
  rebase -i: rewrite the edit-todo functionality in C
  sequencer: add a new function to silence a command, except if it fails
  rebase -i: rewrite setup_reflog_action() in C
  rebase -i: rewrite checkout_onto() in C
  sequencer: refactor append_todo_help() to write its message to a
buffer
  sequencer: change the way skip_unnecessary_picks() returns its result
  t3404: todo list with commented-out commands only aborts
  rebase -i: rewrite complete_action() in C
  rebase -i: remove unused modes and functions
  rebase -i: implement the logic to initialize $revisions in C
  rebase -i: rewrite the rest of init_revisions_and_shortrevisions() in
C
  rebase -i: rewrite write_basic_state() in C
  rebase -i: rewrite init_basic_state() in C
  rebase -i: implement the main part of interactive rebase as a builtin
  rebase--interactive2: rewrite the submodes of interactive rebase in C
  rebase -i: remove git-rebase--interactive.sh
  rebase -i: move rebase--helper modes to rebase--interactive

 .gitignore |   1 -
 Makefile   |   5 +-
 builtin.h  |   2 +-
 builtin/rebase--helper.c   |  88 -
 builtin/rebase--interactive.c  | 271 
 cache.h|   1 +
 editor.c   |  27 ++-
 git-rebase--interactive.sh | 283 -
 git-rebase--preserve-merges.sh |  10 +-
 git-rebase.sh  |  47 -
 git.c  |   2 +-
 rebase-interactive.c   |  90 ++
 rebase-interactive.h   |   8 +
 sequencer.c| 320 +++--
 sequencer.h|  22 ++-
 strbuf.h   |   2 +
 t/t3404-rebase-interactive.sh  |  10 ++
 17 files changed, 740 insertions(+), 449 deletions(-)
 delete mode 100644 builtin/rebase--helper.c
 create mode 100644 builtin/rebase--interactive.c
 delete mode 100644 git-rebase--interactive.sh
 create mode 100644 rebase-interactive.c
 create mode 100644 rebase-interactive.h

-- 
2.19.0



[GSoC][PATCH v8 01/20] sequencer: make three functions and an enum from sequencer.c public

2018-09-27 Thread Alban Gruin
This makes rebase_path_todo(), get_missing_commit_check_level(),
write_message() and the enum check_level accessible outside sequencer.c,
renames check_level to missing_commit_check_level, and prefixes its
value names by MISSING_COMMIT_ to avoid namespace pollution.

This function and this enum will eventually be moved to
rebase-interactive.c and become static again, so no special attention
was given to the naming.

This will be needed for the rewrite of append_todo_help() from shell to
C, as it will be in a new library source file, rebase-interactive.c.

Signed-off-by: Alban Gruin 
---
No changes since v7.

 sequencer.c | 26 +++---
 sequencer.h | 11 +++
 2 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index ddb41a62d9..c24d37dfb1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -53,7 +53,7 @@ static GIT_PATH_FUNC(rebase_path, "rebase-merge")
  * the lines are processed, they are removed from the front of this
  * file and written to the tail of 'done'.
  */
-static GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
+GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
 /*
  * The rebase command lines that have already been processed. A line
  * is moved here when it is first handled, before any associated user
@@ -377,8 +377,8 @@ static void print_advice(int show_hint, struct replay_opts 
*opts)
}
 }
 
-static int write_message(const void *buf, size_t len, const char *filename,
-int append_eol)
+int write_message(const void *buf, size_t len, const char *filename,
+ int append_eol)
 {
struct lock_file msg_file = LOCK_INIT;
 
@@ -4422,24 +4422,20 @@ int transform_todos(unsigned flags)
return i;
 }
 
-enum check_level {
-   CHECK_IGNORE = 0, CHECK_WARN, CHECK_ERROR
-};
-
-static enum check_level get_missing_commit_check_level(void)
+enum missing_commit_check_level get_missing_commit_check_level(void)
 {
const char *value;
 
if (git_config_get_value("rebase.missingcommitscheck", ) ||
!strcasecmp("ignore", value))
-   return CHECK_IGNORE;
+   return MISSING_COMMIT_CHECK_IGNORE;
if (!strcasecmp("warn", value))
-   return CHECK_WARN;
+   return MISSING_COMMIT_CHECK_WARN;
if (!strcasecmp("error", value))
-   return CHECK_ERROR;
+   return MISSING_COMMIT_CHECK_ERROR;
warning(_("unrecognized setting %s for option "
  "rebase.missingCommitsCheck. Ignoring."), value);
-   return CHECK_IGNORE;
+   return MISSING_COMMIT_CHECK_IGNORE;
 }
 
 define_commit_slab(commit_seen, unsigned char);
@@ -4451,7 +4447,7 @@ define_commit_slab(commit_seen, unsigned char);
  */
 int check_todo_list(void)
 {
-   enum check_level check_level = get_missing_commit_check_level();
+   enum missing_commit_check_level check_level = 
get_missing_commit_check_level();
struct strbuf todo_file = STRBUF_INIT;
struct todo_list todo_list = TODO_LIST_INIT;
struct strbuf missing = STRBUF_INIT;
@@ -4468,7 +4464,7 @@ int check_todo_list(void)
advise_to_edit_todo = res =
parse_insn_buffer(todo_list.buf.buf, _list);
 
-   if (res || check_level == CHECK_IGNORE)
+   if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
goto leave_check;
 
/* Mark the commits in git-rebase-todo as seen */
@@ -4503,7 +4499,7 @@ int check_todo_list(void)
if (!missing.len)
goto leave_check;
 
-   if (check_level == CHECK_ERROR)
+   if (check_level == MISSING_COMMIT_CHECK_ERROR)
advise_to_edit_todo = res = 1;
 
fprintf(stderr,
diff --git a/sequencer.h b/sequencer.h
index c986bc8251..579de9127b 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -8,6 +8,7 @@ struct commit;
 
 const char *git_path_commit_editmsg(void);
 const char *git_path_seq_dir(void);
+const char *rebase_path_todo(void);
 
 #define APPEND_SIGNOFF_DEDUP (1u << 0)
 
@@ -62,6 +63,15 @@ struct replay_opts {
 };
 #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
 
+enum missing_commit_check_level {
+   MISSING_COMMIT_CHECK_IGNORE = 0,
+   MISSING_COMMIT_CHECK_WARN,
+   MISSING_COMMIT_CHECK_ERROR
+};
+
+int write_message(const void *buf, size_t len, const char *filename,
+ int append_eol);
+
 /* Call this to setup defaults before parsing command line options */
 void sequencer_init_config(struct replay_opts *opts);
 int sequencer_pick_revisions(struct replay_opts *opts);
@@ -84,6 +94,7 @@ int sequencer_make_script(FILE *out, int argc, const char 
**argv,
 
 int sequencer_add_exec_commands(const char *command);
 int transform_todos(unsigned flags);
+enum missing_commit_check_level get_missing_commit_check_level(

[GSoC][PATCH v7 10/20] t3404: todo list with commented-out commands only aborts

2018-08-28 Thread Alban Gruin
If the todo list generated by `--make-script` is empty,
complete_action() writes a noop, but if it has only commented-out
commands, it will abort with the message "Nothing to do", and does not
launch the editor.  This adds a new test to ensure that
complete_action() behaves this way.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 t/t3404-rebase-interactive.sh | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 01616901bd..496d88d7d6 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -75,6 +75,16 @@ test_expect_success 'rebase --keep-empty' '
test_line_count = 6 actual
 '
 
+cat > expect <actual 
2>&1 &&
+   test_i18ncmp expect actual
+'
+
 test_expect_success 'rebase -i with the exec command' '
git checkout master &&
(
-- 
2.18.0



[GSoC][PATCH v7 03/20] editor: add a function to launch the sequence editor

2018-08-28 Thread Alban Gruin
As part of the rewrite of interactive rebase, the sequencer will need to
open the sequence editor to allow the user to edit the todo list.
Instead of duplicating the existing launch_editor() function, this
refactors it to a new function, launch_specified_editor(), which takes
the editor as a parameter, in addition to the path, the buffer and the
environment variables.  launch_sequence_editor() is then added to launch
the sequence editor.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 cache.h  |  1 +
 editor.c | 27 +--
 strbuf.h |  2 ++
 3 files changed, 28 insertions(+), 2 deletions(-)

diff --git a/cache.h b/cache.h
index 8b447652a7..d70ae49ca2 100644
--- a/cache.h
+++ b/cache.h
@@ -1409,6 +1409,7 @@ extern const char *fmt_name(const char *name, const char 
*email);
 extern const char *ident_default_name(void);
 extern const char *ident_default_email(void);
 extern const char *git_editor(void);
+extern const char *git_sequence_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int is_terminal_dumb(void);
 extern int git_ident_config(const char *, const char *, void *);
diff --git a/editor.c b/editor.c
index 9a9b4e12d1..c985eee1f9 100644
--- a/editor.c
+++ b/editor.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "config.h"
 #include "strbuf.h"
 #include "run-command.h"
 #include "sigchain.h"
@@ -34,10 +35,21 @@ const char *git_editor(void)
return editor;
 }
 
-int launch_editor(const char *path, struct strbuf *buffer, const char *const 
*env)
+const char *git_sequence_editor(void)
 {
-   const char *editor = git_editor();
+   const char *editor = getenv("GIT_SEQUENCE_EDITOR");
+
+   if (!editor)
+   git_config_get_string_const("sequence.editor", );
+   if (!editor)
+   editor = git_editor();
 
+   return editor;
+}
+
+static int launch_specified_editor(const char *editor, const char *path,
+  struct strbuf *buffer, const char *const 
*env)
+{
if (!editor)
return error("Terminal is dumb, but EDITOR unset");
 
@@ -95,3 +107,14 @@ int launch_editor(const char *path, struct strbuf *buffer, 
const char *const *en
return error_errno("could not read file '%s'", path);
return 0;
 }
+
+int launch_editor(const char *path, struct strbuf *buffer, const char *const 
*env)
+{
+   return launch_specified_editor(git_editor(), path, buffer, env);
+}
+
+int launch_sequence_editor(const char *path, struct strbuf *buffer,
+  const char *const *env)
+{
+   return launch_specified_editor(git_sequence_editor(), path, buffer, 
env);
+}
diff --git a/strbuf.h b/strbuf.h
index 60a35aef16..66da9822fd 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -575,6 +575,8 @@ extern void strbuf_add_unique_abbrev(struct strbuf *sb,
  * 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);
+extern 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);
 
-- 
2.18.0



[GSoC][PATCH v7 04/20] rebase -i: rewrite the edit-todo functionality in C

2018-08-28 Thread Alban Gruin
This rewrites the edit-todo functionality from shell to C.

To achieve that, a new command mode, `edit-todo`, is added, and the
`write-edit-todo` flag is removed, as the shell script does not need to
write the edit todo help message to the todo list anymore.

The shell version is then stripped in favour of a call to the helper.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c   | 13 -
 git-rebase--interactive.sh | 11 +--
 rebase-interactive.c   | 27 +++
 rebase-interactive.h   |  1 +
 4 files changed, 37 insertions(+), 15 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 05e73e71d4..731a64971d 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -13,12 +13,12 @@ static const char * const builtin_rebase_helper_usage[] = {
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
-   unsigned flags = 0, keep_empty = 0, rebase_merges = 0, write_edit_todo 
= 0;
+   unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
int abbreviate_commands = 0, rebase_cousins = -1;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC, APPEND_TODO_HELP
+   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -28,8 +28,6 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
OPT_BOOL(0, "rebase-merges", _merges, N_("rebase merge 
commits")),
OPT_BOOL(0, "rebase-cousins", _cousins,
 N_("keep original branch points of cousins")),
-   OPT_BOOL(0, "write-edit-todo", _edit_todo,
-N_("append the edit-todo message to the todo-list")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
OPT_CMDMODE(0, "abort", , N_("abort rebase"),
@@ -50,6 +48,9 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("insert exec commands in todo list"), ADD_EXEC),
OPT_CMDMODE(0, "append-todo-help", ,
N_("insert the help in the todo list"), 
APPEND_TODO_HELP),
+   OPT_CMDMODE(0, "edit-todo", ,
+   N_("edit the todo list during an interactive 
rebase"),
+   EDIT_TODO),
OPT_END()
};
 
@@ -90,6 +91,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (command == ADD_EXEC && argc == 2)
return !!sequencer_add_exec_commands(argv[1]);
if (command == APPEND_TODO_HELP && argc == 1)
-   return !!append_todo_help(write_edit_todo, keep_empty);
+   return !!append_todo_help(0, keep_empty);
+   if (command == EDIT_TODO && argc == 1)
+   return !!edit_todo_list(flags);
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 94c23a7af2..2defe607f4 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -108,16 +108,7 @@ initiate_action () {
 --continue
;;
edit-todo)
-   git stripspace --strip-comments <"$todo" >"$todo".new
-   mv -f "$todo".new "$todo"
-   collapse_todo_ids
-   git rebase--helper --append-todo-help --write-edit-todo
-
-   git_sequence_editor "$todo" ||
-   die "$(gettext "Could not execute editor")"
-   expand_todo_ids
-
-   exit
+   exec git rebase--helper --edit-todo
;;
show-current-patch)
exec git show REBASE_HEAD --
diff --git a/rebase-interactive.c b/rebase-interactive.c
index d7996bc8d9..3f9468fc69 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -66,3 +66,30 @@ int append_todo_help(unsigned edit_todo, unsigned keep_empty)
 
return ret;
 }
+
+int edit_todo_list(unsigned flags)
+{
+   struct strbuf buf = STRBUF_INIT;
+   const char *todo_file = rebase_path_todo();
+
+   if (strbuf_read_file(, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   strbuf_stripspace(, 1);
+   if (write_message(buf.buf, buf.len, todo_file, 0)) {
+   str

[GSoC][PATCH v7 20/20] rebase -i: move rebase--helper modes to rebase--interactive

2018-08-28 Thread Alban Gruin
This moves the rebase--helper modes still used by
git-rebase--preserve-merges.sh (`--shorten-ids`, `--expand-ids`,
`--check-todo-list`, `--rearrange-squash` and `--add-exec-commands`) to
rebase--interactive.c.

git-rebase--preserve-merges.sh is modified accordingly, and
rebase--helper.c is removed as it is useless now.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 .gitignore |   1 -
 Makefile   |   1 -
 builtin.h  |   1 -
 builtin/rebase--helper.c   | 226 -
 builtin/rebase--interactive.c  |  27 +++-
 git-rebase--preserve-merges.sh |  10 +-
 git.c  |   1 -
 7 files changed, 31 insertions(+), 236 deletions(-)
 delete mode 100644 builtin/rebase--helper.c

diff --git a/.gitignore b/.gitignore
index 3284a1e9b1..406f26d050 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,7 +116,6 @@
 /git-read-tree
 /git-rebase
 /git-rebase--am
-/git-rebase--helper
 /git-rebase--interactive
 /git-rebase--merge
 /git-rebase--preserve-merges
diff --git a/Makefile b/Makefile
index 584834726d..ca3a0888dd 100644
--- a/Makefile
+++ b/Makefile
@@ -1059,7 +1059,6 @@ BUILTIN_OBJS += builtin/prune.o
 BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/read-tree.o
-BUILTIN_OBJS += builtin/rebase--helper.o
 BUILTIN_OBJS += builtin/rebase--interactive.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index ed89b4f495..7feb689d87 100644
--- a/builtin.h
+++ b/builtin.h
@@ -203,7 +203,6 @@ extern int cmd_pull(int argc, const char **argv, const char 
*prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_rebase__interactive(int argc, const char **argv, const char 
*prefix);
-extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
deleted file mode 100644
index f8128037d3..00
--- a/builtin/rebase--helper.c
+++ /dev/null
@@ -1,226 +0,0 @@
-#include "builtin.h"
-#include "cache.h"
-#include "config.h"
-#include "parse-options.h"
-#include "sequencer.h"
-#include "rebase-interactive.h"
-#include "argv-array.h"
-#include "refs.h"
-#include "rerere.h"
-#include "alias.h"
-
-static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
-static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
-static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
-
-static int get_revision_ranges(const char *upstream, const char *onto,
-  const char **head_hash,
-  char **revisions, char **shortrevisions)
-{
-   const char *base_rev = upstream ? upstream : onto;
-   struct object_id orig_head;
-
-   if (get_oid("HEAD", _head))
-   return error(_("no HEAD?"));
-
-   *head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
-
-   if (revisions)
-   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
-   if (shortrevisions) {
-   const char *shorthead;
-
-   shorthead = find_unique_abbrev(_head, DEFAULT_ABBREV);
-
-   if (upstream) {
-   const char *shortrev;
-   struct object_id rev_oid;
-
-   get_oid(base_rev, _oid);
-   shortrev = find_unique_abbrev(_oid, DEFAULT_ABBREV);
-
-   *shortrevisions = xstrfmt("%s..%s", shortrev, 
shorthead);
-   } else
-   *shortrevisions = xstrdup(shorthead);
-   }
-
-   return 0;
-}
-
-static int init_basic_state(struct replay_opts *opts, const char *head_name,
-   const char *onto, const char *orig_head)
-{
-   FILE *interactive;
-
-   if (!is_directory(path_state_dir()) && 
mkdir_in_gitdir(path_state_dir()))
-   return error_errno(_("could not create temporary %s"), 
path_state_dir());
-
-   delete_reflog("REBASE_HEAD");
-
-   interactive = fopen(path_interactive(), "w");
-   if (!interactive)
-   return error_errno(_("could not mark as interactive"));
-   fclose(interactive);
-
-   return write_basic_state(opts, head_name, onto, orig_head);
-}
-
-static const char * const builtin_rebase_helper_usage[] = {
-   N_("git rebase--helper []"),
-   NULL
-};
-
-int cmd_rebase__helper(int ar

[GSoC][PATCH v7 08/20] sequencer: refactor append_todo_help() to write its message to a buffer

2018-08-28 Thread Alban Gruin
This refactors append_todo_help() to write its message to a buffer
instead of the todo-list.  This is needed for the rewrite of
complete_action(), which will come after the next commit.

As rebase--helper still needs the file manipulation part of
append_todo_help(), it is extracted to a temporary function,
append_todo_help_to_file().  This function will go away after the
rewrite of complete_action().

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c |  2 +-
 rebase-interactive.c | 43 
 rebase-interactive.h |  4 +++-
 3 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 7d9426d23c..313092c465 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -97,7 +97,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (command == ADD_EXEC && argc == 2)
return !!sequencer_add_exec_commands(argv[1]);
if (command == APPEND_TODO_HELP && argc == 1)
-   return !!append_todo_help(0, keep_empty);
+   return !!append_todo_help_to_file(0, keep_empty);
if (command == EDIT_TODO && argc == 1)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
diff --git a/rebase-interactive.c b/rebase-interactive.c
index 3f9468fc69..4a9a10eff4 100644
--- a/rebase-interactive.c
+++ b/rebase-interactive.c
@@ -4,11 +4,9 @@
 #include "sequencer.h"
 #include "strbuf.h"
 
-int append_todo_help(unsigned edit_todo, unsigned keep_empty)
+void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+ struct strbuf *buf)
 {
-   struct strbuf buf = STRBUF_INIT;
-   FILE *todo;
-   int ret;
const char *msg = _("\nCommands:\n"
 "p, pick  = use commit\n"
 "r, reword  = use commit, but edit the commit message\n"
@@ -26,11 +24,7 @@ int append_todo_help(unsigned edit_todo, unsigned keep_empty)
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
 
-   todo = fopen_or_warn(rebase_path_todo(), "a");
-   if (!todo)
-   return 1;
-
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
 
if (get_missing_commit_check_level() == MISSING_COMMIT_CHECK_ERROR)
msg = _("\nDo not remove any line. Use 'drop' "
@@ -39,7 +33,7 @@ int append_todo_help(unsigned edit_todo, unsigned keep_empty)
msg = _("\nIf you remove a line here "
 "THAT COMMIT WILL BE LOST.\n");
 
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
 
if (edit_todo)
msg = _("\nYou are editing the todo file "
@@ -50,12 +44,25 @@ int append_todo_help(unsigned edit_todo, unsigned 
keep_empty)
msg = _("\nHowever, if you remove everything, "
"the rebase will be aborted.\n\n");
 
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
 
if (!keep_empty) {
msg = _("Note that empty commits are commented out");
-   strbuf_add_commented_lines(, msg, strlen(msg));
+   strbuf_add_commented_lines(buf, msg, strlen(msg));
}
+}
+
+int append_todo_help_to_file(unsigned edit_todo, unsigned keep_empty)
+{
+   struct strbuf buf = STRBUF_INIT;
+   FILE *todo;
+   int ret;
+
+   todo = fopen_or_warn(rebase_path_todo(), "a");
+   if (!todo)
+   return -1;
+
+   append_todo_help(edit_todo, keep_empty, );
 
ret = fputs(buf.buf, todo);
if (ret < 0)
@@ -84,7 +91,17 @@ int edit_todo_list(unsigned flags)
strbuf_release();
 
transform_todos(flags | TODO_LIST_SHORTEN_IDS);
-   append_todo_help(1, 0);
+
+   if (strbuf_read_file(, todo_file, 0) < 0)
+   return error_errno(_("could not read '%s'."), todo_file);
+
+   append_todo_help(1, 0, );
+   if (write_message(buf.buf, buf.len, todo_file, 0)) {
+   strbuf_release();
+   return -1;
+   }
+
+   strbuf_release();
 
if (launch_sequence_editor(todo_file, NULL, NULL))
return -1;
diff --git a/rebase-interactive.h b/rebase-interactive.h
index 155219e742..d33f3176b7 100644
--- a/rebase-interactive.h
+++ b/rebase-interactive.h
@@ -1,7 +1,9 @@
 #ifndef REBASE_INTERACTIVE_H
 #define REBASE_INTERACTIVE_H
 
-int append_todo_help(unsigned edit_todo, unsigned keep_empty);
+void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+ struct strbuf *buf);
+int append_todo_help_to_file(unsigned edit_todo, unsigned keep_empty);
 int edit_todo_list(unsigned flags);
 
 #endif
-- 
2.18.0



[GSoC][PATCH v7 11/20] rebase -i: rewrite complete_action() in C

2018-08-28 Thread Alban Gruin
This rewrites complete_action() from shell to C.

A new mode is added to rebase--helper (`--complete-action`), as well as
a new flag (`--autosquash`).

Finally, complete_action() is stripped from git-rebase--interactive.sh.

The original complete_action() would return the code 2 when the todo
list contained no actions.  This was a special case for rebase -i and
-p; git-rebase.sh would then apply the autostash, delete the state
directory, and die with the message "Nothing to do".  This cleanup is
rewritten in C instead of returning 2.  As rebase -i no longer returns
2, the comment describing this behaviour in git-rebase.sh is updated to
reflect this change.

The message "Nothing to do" is now printed with error(), and so becomes
"error: nothing to do".  Some tests in t3404 check this value, so they
are updated to fit this change.

The first check might seem useless as we write "noop" to the todo list
if it is empty.  Actually, the todo list might contain commented
commands (ie. empty commits).  In this case, complete_action() won’t
write "noop", and will abort without starting the editor.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--helper.c  |  12 +++-
 git-rebase--interactive.sh|  53 ++---
 git-rebase.sh |   2 +-
 sequencer.c   | 104 ++
 sequencer.h   |   4 ++
 t/t3404-rebase-interactive.sh |   2 +-
 6 files changed, 124 insertions(+), 53 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index bed3dd2b95..01b958 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -13,13 +13,13 @@ static const char * const builtin_rebase_helper_usage[] = {
 int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
-   unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
+   unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
int abbreviate_commands = 0, rebase_cousins = -1;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH,
-   CHECKOUT_ONTO
+   CHECKOUT_ONTO, COMPLETE_ACTION
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -29,6 +29,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
OPT_BOOL(0, "rebase-merges", _merges, N_("rebase merge 
commits")),
OPT_BOOL(0, "rebase-cousins", _cousins,
 N_("keep original branch points of cousins")),
+   OPT_BOOL(0, "autosquash", ,
+N_("move commits that begin with squash!/fixup!")),
OPT__VERBOSE(, N_("be verbose")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
@@ -57,6 +59,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
OPT_CMDMODE(0, "checkout-onto", ,
N_("checkout a commit"), CHECKOUT_ONTO),
+   OPT_CMDMODE(0, "complete-action", ,
+   N_("complete the action"), COMPLETE_ACTION),
OPT_END()
};
 
@@ -110,5 +114,9 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!prepare_branch_to_be_rebased(, argv[1]);
if (command == CHECKOUT_ONTO && argc == 4)
return !!checkout_onto(, argv[1], argv[2], argv[3]);
+   if (command == COMPLETE_ACTION && argc == 6)
+   return !!complete_action(, flags, argv[1], argv[2], 
argv[3],
+argv[4], argv[5], autosquash);
+
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index b68f108f28..59dc4888a6 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -127,54 +127,6 @@ init_revisions_and_shortrevisions () {
fi
 }
 
-complete_action() {
-   test -s "$todo" || echo noop >> "$todo"
-   test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
-   test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
-
-   todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
-   todocount=${todocount##* }
-
-cat >>"$todo&q

[GSoC][PATCH v7 15/20] rebase -i: rewrite write_basic_state() in C

2018-08-28 Thread Alban Gruin
This rewrites write_basic_state() from git-rebase.sh in C.  This is the
first step in the conversion of init_basic_state(), hence the mode in
rebase--helper.c is called INIT_BASIC_STATE.  init_basic_state() will be
converted in the next commit.

The part of read_strategy_opts() that parses the stategy options is
moved to a new function to allow its use in rebase--helper.c.

Finally, the call to write_basic_state() is removed from
git-rebase--interactive.sh, replaced by a call to `--init-basic-state`.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c   | 28 +-
 git-rebase--interactive.sh |  7 +++-
 sequencer.c| 77 --
 sequencer.h|  4 ++
 4 files changed, 102 insertions(+), 14 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 0716bbfd78..63c5086e42 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -5,6 +5,8 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 #include "argv-array.h"
+#include "rerere.h"
+#include "alias.h"
 
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 
@@ -53,11 +55,12 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
int abbreviate_commands = 0, rebase_cousins = -1, ret;
const char *head_hash = NULL, *onto = NULL, *restrict_revision = NULL,
-   *squash_onto = NULL, *upstream = NULL;
+   *squash_onto = NULL, *upstream = NULL, *head_name = NULL;
+   char *raw_strategies = NULL;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC, EDIT_TODO, 
PREPARE_BRANCH,
-   COMPLETE_ACTION
+   COMPLETE_ACTION, INIT_BASIC_STATE
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -69,6 +72,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
 N_("keep original branch points of cousins")),
OPT_BOOL(0, "autosquash", ,
 N_("move commits that begin with squash!/fixup!")),
+   OPT_BOOL(0, "signoff", , N_("sign commits")),
OPT__VERBOSE(, N_("be verbose")),
OPT_CMDMODE(0, "continue", , N_("continue rebase"),
CONTINUE),
@@ -93,6 +97,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
OPT_CMDMODE(0, "complete-action", ,
N_("complete the action"), COMPLETE_ACTION),
+   OPT_CMDMODE(0, "init-basic-state", ,
+   N_("initialise the rebase state"), 
INIT_BASIC_STATE),
OPT_STRING(0, "onto", , N_("onto"), N_("onto")),
OPT_STRING(0, "restrict-revision", _revision,
   N_("restrict-revision"), N_("restrict revision")),
@@ -100,6 +106,14 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
   N_("squash onto")),
OPT_STRING(0, "upstream", , N_("upstream"),
   N_("the upstream commit")),
+   OPT_STRING(0, "head-name", _name, N_("head-name"), 
N_("head name")),
+   OPT_STRING('S', "gpg-sign", _sign, N_("gpg-sign"),
+  N_("GPG-sign commits")),
+   OPT_STRING(0, "strategy", , N_("strategy"),
+  N_("rebase strategy")),
+   OPT_STRING(0, "strategy-opts", _strategies, 
N_("strategy-opts"),
+  N_("strategy options")),
+   OPT_RERERE_AUTOUPDATE(_rerere_auto),
OPT_END()
};
 
@@ -176,6 +190,16 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
free(shortrevisions);
return !!ret;
}
+   if (command == INIT_BASIC_STATE) {
+   if (raw_strategies)
+   parse_strategy_opts(, raw_strategies);
+
+   ret = get_revision_ranges(upstream, onto, _hash, NULL, 
NULL);
+   if (ret)
+   return ret;
+
+   return !!write_basic_state(, head_name, onto, head_hash);
+   }
 
usage_with_opti

[GSoC][PATCH v7 12/20] rebase -i: remove unused modes and functions

2018-08-28 Thread Alban Gruin
This removes the modes `--skip-unnecessary-picks`, `--append-todo-help`,
and `--checkout-onto` from rebase--helper.c, the functions of
git-rebase--interactive.sh that were rendered useless by the rewrite of
complete_action(), and append_todo_help_to_file() from
rebase-interactive.c.

skip_unnecessary_picks() and checkout_onto() becomes static, as they are
only used inside of the sequencer.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c   | 23 ++
 git-rebase--interactive.sh | 50 --
 rebase-interactive.c   | 22 -
 rebase-interactive.h   |  1 -
 sequencer.c|  8 +++---
 sequencer.h|  4 ---
 6 files changed, 6 insertions(+), 102 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 01b958..e1460136f5 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -17,9 +17,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
int abbreviate_commands = 0, rebase_cousins = -1;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
-   CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH,
-   CHECKOUT_ONTO, COMPLETE_ACTION
+   CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC, EDIT_TODO, 
PREPARE_BRANCH,
+   COMPLETE_ACTION
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -44,21 +43,15 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("expand commit ids in the todo list"), EXPAND_OIDS),
OPT_CMDMODE(0, "check-todo-list", ,
N_("check the todo list"), CHECK_TODO_LIST),
-   OPT_CMDMODE(0, "skip-unnecessary-picks", ,
-   N_("skip unnecessary picks"), SKIP_UNNECESSARY_PICKS),
OPT_CMDMODE(0, "rearrange-squash", ,
N_("rearrange fixup/squash lines"), REARRANGE_SQUASH),
OPT_CMDMODE(0, "add-exec-commands", ,
N_("insert exec commands in todo list"), ADD_EXEC),
-   OPT_CMDMODE(0, "append-todo-help", ,
-   N_("insert the help in the todo list"), 
APPEND_TODO_HELP),
OPT_CMDMODE(0, "edit-todo", ,
N_("edit the todo list during an interactive 
rebase"),
EDIT_TODO),
OPT_CMDMODE(0, "prepare-branch", ,
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
-   OPT_CMDMODE(0, "checkout-onto", ,
-   N_("checkout a commit"), CHECKOUT_ONTO),
OPT_CMDMODE(0, "complete-action", ,
N_("complete the action"), COMPLETE_ACTION),
OPT_END()
@@ -94,26 +87,14 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!transform_todos(flags);
if (command == CHECK_TODO_LIST && argc == 1)
return !!check_todo_list();
-   if (command == SKIP_UNNECESSARY_PICKS && argc == 1) {
-   struct object_id oid;
-   int ret = skip_unnecessary_picks();
-
-   if (!ret)
-   puts(oid_to_hex());
-   return !!ret;
-   }
if (command == REARRANGE_SQUASH && argc == 1)
return !!rearrange_squash();
if (command == ADD_EXEC && argc == 2)
return !!sequencer_add_exec_commands(argv[1]);
-   if (command == APPEND_TODO_HELP && argc == 1)
-   return !!append_todo_help_to_file(0, keep_empty);
if (command == EDIT_TODO && argc == 1)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
return !!prepare_branch_to_be_rebased(, argv[1]);
-   if (command == CHECKOUT_ONTO && argc == 4)
-   return !!checkout_onto(, argv[1], argv[2], argv[3]);
if (command == COMPLETE_ACTION && argc == 6)
return !!complete_action(, flags, argv[1], argv[2], 
argv[3],
 argv[4], argv[5], autosquash);
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 59dc4888a6..0d66c0f8b8 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -16,56 +16,6 @@ todo="$state_dir"/git-rebase-todo
 GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_

[GSoC][PATCH v7 18/20] rebase--interactive2: rewrite the submodes of interactive rebase in C

2018-08-28 Thread Alban Gruin
This rewrites the submodes of interactive rebase (`--continue`,
`--skip`, `--edit-todo`, and `--show-current-patch`) in C.

git-rebase.sh is then modified to call directly git-rebase--interactive2
instead of git-rebase--interactive.sh.

Signed-off-by: Alban Gruin 
---
 builtin/rebase--interactive2.c | 51 ++
 git-rebase.sh  | 45 +++---
 2 files changed, 86 insertions(+), 10 deletions(-)

diff --git a/builtin/rebase--interactive2.c b/builtin/rebase--interactive2.c
index 038bbf359e..b32621b179 100644
--- a/builtin/rebase--interactive2.c
+++ b/builtin/rebase--interactive2.c
@@ -134,11 +134,14 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
-   int abbreviate_commands = 0, rebase_cousins = -1;
+   int abbreviate_commands = 0, rebase_cousins = -1, ret = 0;
const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
*squash_onto = NULL, *upstream = NULL, *head_name = NULL,
*switch_to = NULL, *cmd = NULL;
char *raw_strategies = NULL;
+   enum {
+   NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH
+   } command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
OPT_BOOL(0, "keep-empty", _empty, N_("keep empty 
commits")),
@@ -151,6 +154,13 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
 N_("move commits that begin with squash!/fixup!")),
OPT_BOOL(0, "signoff", , N_("sign commits")),
OPT__VERBOSE(, N_("be verbose")),
+   OPT_CMDMODE(0, "continue", , N_("continue rebase"),
+   CONTINUE),
+   OPT_CMDMODE(0, "skip", , N_("skip commit"), SKIP),
+   OPT_CMDMODE(0, "edit-todo", , N_("edit the todo list"),
+   EDIT_TODO),
+   OPT_CMDMODE(0, "show-current-patch", , N_("show the 
current patch"),
+   SHOW_CURRENT_PATCH),
OPT_STRING(0, "onto", , N_("onto"), N_("onto")),
OPT_STRING(0, "restrict-revision", _revision,
   N_("restrict-revision"), N_("restrict revision")),
@@ -197,10 +207,39 @@ int cmd_rebase__interactive(int argc, const char **argv, 
const char *prefix)
warning(_("--[no-]rebase-cousins has no effect without "
  "--rebase-merges"));
 
-   if (!onto && !upstream)
-   die(_("a base commit must be provided with --upstream or 
--onto"));
+   switch (command) {
+   case NONE:
+   if (!onto && !upstream)
+   die(_("a base commit must be provided with --upstream 
or --onto"));
+
+   ret = do_interactive_rebase(, flags, switch_to, upstream, 
onto,
+   onto_name, squash_onto, head_name, 
restrict_revision,
+   raw_strategies, cmd, autosquash);
+   break;
+   case SKIP: {
+   struct string_list merge_rr = STRING_LIST_INIT_DUP;
+
+   rerere_clear(_rr);
+   /* fallthrough */
+   case CONTINUE:
+   ret = sequencer_continue();
+   break;
+   }
+   case EDIT_TODO:
+   ret = edit_todo_list(flags);
+   break;
+   case SHOW_CURRENT_PATCH: {
+   struct child_process cmd = CHILD_PROCESS_INIT;
+
+   cmd.git_cmd = 1;
+   argv_array_pushl(, "show", "REBASE_HEAD", "--", NULL);
+   ret = run_command();
+
+   break;
+   }
+   default:
+   BUG("invalid command '%d'", command);
+   }
 
-   return !!do_interactive_rebase(, flags, switch_to, upstream, onto,
-  onto_name, squash_onto, head_name, 
restrict_revision,
-  raw_strategies, cmd, autosquash);
+   return !!ret;
 }
diff --git a/git-rebase.sh b/git-rebase.sh
index 51a6db7daa..6e1e413cf2 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -200,19 +200,56 @@ finish_rebase () {
rm -rf "$state_dir"
 }
 
+run_interactive () {
+   GIT_CHERRY_PICK_HELP="$resolvemsg"
+   export GIT_CHERRY_PICK_HELP
+
+   test -n "$keep_empty" && keep_empty="--keep-empty"
+   test -n "$rebase_merges&q

[GSoC][PATCH v7 16/20] rebase -i: rewrite init_basic_state() in C

2018-08-28 Thread Alban Gruin
This rewrites init_basic_state() from shell to C.  The call to
write_basic_state() in cmd_rebase__helper() is replaced by a call to the
new function.

The shell version is then stripped from git-rebase--interactive.sh.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c   | 23 ++-
 git-rebase--interactive.sh |  9 -
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 63c5086e42..f8128037d3 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -5,10 +5,13 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 #include "argv-array.h"
+#include "refs.h"
 #include "rerere.h"
 #include "alias.h"
 
+static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
@@ -44,6 +47,24 @@ static int get_revision_ranges(const char *upstream, const 
char *onto,
return 0;
 }
 
+static int init_basic_state(struct replay_opts *opts, const char *head_name,
+   const char *onto, const char *orig_head)
+{
+   FILE *interactive;
+
+   if (!is_directory(path_state_dir()) && 
mkdir_in_gitdir(path_state_dir()))
+   return error_errno(_("could not create temporary %s"), 
path_state_dir());
+
+   delete_reflog("REBASE_HEAD");
+
+   interactive = fopen(path_interactive(), "w");
+   if (!interactive)
+   return error_errno(_("could not mark as interactive"));
+   fclose(interactive);
+
+   return write_basic_state(opts, head_name, onto, orig_head);
+}
+
 static const char * const builtin_rebase_helper_usage[] = {
N_("git rebase--helper []"),
NULL
@@ -198,7 +219,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (ret)
return ret;
 
-   return !!write_basic_state(, head_name, onto, head_hash);
+   return !!init_basic_state(, head_name, onto, head_hash);
}
 
usage_with_options(builtin_rebase_helper_usage, options);
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 6367da66e2..761be95ed1 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -51,14 +51,6 @@ initiate_action () {
esac
 }
 
-init_basic_state () {
-   orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
-   mkdir -p "$state_dir" || die "$(eval_gettext "Could not create 
temporary \$state_dir")"
-   rm -f "$(git rev-parse --git-path REBASE_HEAD)"
-
-   : > "$state_dir"/interactive || die "$(gettext "Could not mark as 
interactive")"
-}
-
 git_rebase__interactive () {
initiate_action "$action"
ret=$?
@@ -67,7 +59,6 @@ git_rebase__interactive () {
fi
 
git rebase--helper --prepare-branch "$switch_to" ${verbose:+--verbose}
-   init_basic_state
 
git rebase--helper --init-basic-state ${upstream:+--upstream 
"$upstream"} \
${onto:+--onto "$onto"} ${head_name:+--head-name "$head_name"} \
-- 
2.18.0



[GSoC][PATCH v7 13/20] rebase -i: implement the logic to initialize $revisions in C

2018-08-28 Thread Alban Gruin
This rewrites the part of init_revisions_and_shortrevisions() needed by
`--make-script` from shell to C.  The new version is called
get_revision_ranges(), and is a static function inside of
rebase--helper.c.  As this does not initialize $shortrevision, the
original shell version is not yet stripped.

Unlike init_revisions_and_shortrevisions(), get_revision_ranges()
doesn’t write $squash_onto to the state directory, it’s done by the
handler of `--make-script` instead.

Finally, this drops the $revision argument passed to `--make-script` in
git-rebase--interactive.sh, and rebase--helper is changed accordingly.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c   | 56 --
 git-rebase--interactive.sh |  4 ++-
 2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index e1460136f5..acc71a6f99 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -4,6 +4,25 @@
 #include "parse-options.h"
 #include "sequencer.h"
 #include "rebase-interactive.h"
+#include "argv-array.h"
+
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+
+static int get_revision_ranges(const char *upstream, const char *onto,
+  const char **head_hash,
+  char **revisions)
+{
+   const char *base_rev = upstream ? upstream : onto;
+   struct object_id orig_head;
+
+   if (get_oid("HEAD", _head))
+   return error(_("no HEAD?"));
+
+   *head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
+   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+
+   return 0;
+}
 
 static const char * const builtin_rebase_helper_usage[] = {
N_("git rebase--helper []"),
@@ -14,7 +33,9 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
 {
struct replay_opts opts = REPLAY_OPTS_INIT;
unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
-   int abbreviate_commands = 0, rebase_cousins = -1;
+   int abbreviate_commands = 0, rebase_cousins = -1, ret;
+   const char *head_hash = NULL, *onto = NULL, *restrict_revision = NULL,
+   *squash_onto = NULL, *upstream = NULL;
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, REARRANGE_SQUASH, ADD_EXEC, EDIT_TODO, 
PREPARE_BRANCH,
@@ -54,6 +75,13 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
OPT_CMDMODE(0, "complete-action", ,
N_("complete the action"), COMPLETE_ACTION),
+   OPT_STRING(0, "onto", , N_("onto"), N_("onto")),
+   OPT_STRING(0, "restrict-revision", _revision,
+  N_("restrict-revision"), N_("restrict revision")),
+   OPT_STRING(0, "squash-onto", _onto, N_("squash-onto"),
+  N_("squash onto")),
+   OPT_STRING(0, "upstream", , N_("upstream"),
+  N_("the upstream commit")),
OPT_END()
};
 
@@ -81,8 +109,30 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!sequencer_continue();
if (command == ABORT && argc == 1)
return !!sequencer_remove_state();
-   if (command == MAKE_SCRIPT && argc > 1)
-   return !!sequencer_make_script(stdout, argc, argv, flags);
+   if (command == MAKE_SCRIPT && argc == 1) {
+   char *revisions = NULL;
+   struct argv_array make_script_args = ARGV_ARRAY_INIT;
+
+   if (!upstream && squash_onto)
+   write_file(path_squash_onto(), "%s\n", squash_onto);
+
+   ret = get_revision_ranges(upstream, onto, _hash, 
);
+   if (ret)
+   return ret;
+
+   argv_array_pushl(_script_args, "", revisions, NULL);
+   if (restrict_revision)
+   argv_array_push(_script_args, restrict_revision);
+
+   ret = sequencer_make_script(stdout,
+   make_script_args.argc, 
make_script_args.argv,
+   flags);
+
+   free(revisions);
+   argv_array_clear(_script_args);
+
+   return !!ret;
+   }
if ((command == SHORTEN_OIDS || command == EXPAND_OIDS) && argc == 1)
return !!transform_todos(flags);
if (command == CHECK_TODO_L

[GSoC][PATCH v7 17/20] rebase -i: implement the main part of interactive rebase as a builtin

2018-08-28 Thread Alban Gruin
This rewrites the part of interactive rebase which initializes the
basic state, make the script and complete the action, as a buitin, named
git-rebase--interactive2 for now.  Others modes (`--continue`,
`--edit-todo`, etc.) will be rewritten in the next commit.

git-rebase--interactive.sh is modified to call git-rebase--interactive2
instead of git-rebase--helper.

Signed-off-by: Alban Gruin 
---
 .gitignore |   1 +
 Makefile   |   1 +
 builtin.h  |   1 +
 builtin/rebase--interactive2.c | 206 +
 git-rebase--interactive.sh |  41 ---
 git.c  |   1 +
 6 files changed, 232 insertions(+), 19 deletions(-)
 create mode 100644 builtin/rebase--interactive2.c

diff --git a/.gitignore b/.gitignore
index 3284a1e9b1..404c9a8472 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,6 +118,7 @@
 /git-rebase--am
 /git-rebase--helper
 /git-rebase--interactive
+/git-rebase--interactive2
 /git-rebase--merge
 /git-rebase--preserve-merges
 /git-receive-pack
diff --git a/Makefile b/Makefile
index 909a687857..71f8f45fe5 100644
--- a/Makefile
+++ b/Makefile
@@ -1060,6 +1060,7 @@ BUILTIN_OBJS += builtin/prune.o
 BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/read-tree.o
+BUILTIN_OBJS += builtin/rebase--interactive2.o
 BUILTIN_OBJS += builtin/rebase--helper.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
diff --git a/builtin.h b/builtin.h
index 0362f1ce25..ed89b4f495 100644
--- a/builtin.h
+++ b/builtin.h
@@ -202,6 +202,7 @@ extern int cmd_prune_packed(int argc, const char **argv, 
const char *prefix);
 extern int cmd_pull(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase__interactive(int argc, const char **argv, const char 
*prefix);
 extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
diff --git a/builtin/rebase--interactive2.c b/builtin/rebase--interactive2.c
new file mode 100644
index 00..038bbf359e
--- /dev/null
+++ b/builtin/rebase--interactive2.c
@@ -0,0 +1,206 @@
+#include "builtin.h"
+#include "cache.h"
+#include "config.h"
+#include "parse-options.h"
+#include "sequencer.h"
+#include "rebase-interactive.h"
+#include "argv-array.h"
+#include "refs.h"
+#include "rerere.h"
+#include "run-command.h"
+
+static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
+static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
+static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
+
+static int get_revision_ranges(const char *upstream, const char *onto,
+  const char **head_hash,
+  char **revisions, char **shortrevisions)
+{
+   const char *base_rev = upstream ? upstream : onto, *shorthead;
+   struct object_id orig_head;
+
+   if (get_oid("HEAD", _head))
+   return error(_("no HEAD?"));
+
+   *head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
+   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+
+   shorthead = find_unique_abbrev(_head, DEFAULT_ABBREV);
+
+   if (upstream) {
+   const char *shortrev;
+   struct object_id rev_oid;
+
+   get_oid(base_rev, _oid);
+   shortrev = find_unique_abbrev(_oid, DEFAULT_ABBREV);
+
+   *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead);
+   } else
+   *shortrevisions = xstrdup(shorthead);
+
+   return 0;
+}
+
+static int init_basic_state(struct replay_opts *opts, const char *head_name,
+   const char *onto, const char *orig_head)
+{
+   FILE *interactive;
+
+   if (!is_directory(path_state_dir()) && 
mkdir_in_gitdir(path_state_dir()))
+   return error_errno(_("could not create temporary %s"), 
path_state_dir());
+
+   delete_reflog("REBASE_HEAD");
+
+   interactive = fopen(path_interactive(), "w");
+   if (!interactive)
+   return error_errno(_("could not mark as interactive"));
+   fclose(interactive);
+
+   return write_basic_state(opts, head_name, onto, orig_head);
+}
+
+static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
+const char *switch_to, const char *upstream,
+const char *onto, const char *onto_name,
+const char *squash_onto, const char *

[GSoC][PATCH v7 19/20] rebase -i: remove git-rebase--interactive.sh

2018-08-28 Thread Alban Gruin
This removes git-rebase--interactive.sh, as its functionnality has been
replaced by git-rebase--interactive2.

git-rebase--interactive2.c is then renamed to git-rebase--interactive.c.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 .gitignore|  1 -
 Makefile  |  4 +-
 ...--interactive2.c => rebase--interactive.c} |  0
 git-rebase--interactive.sh| 84 ---
 git-rebase.sh |  2 +-
 git.c |  2 +-
 6 files changed, 3 insertions(+), 90 deletions(-)
 rename builtin/{rebase--interactive2.c => rebase--interactive.c} (100%)
 delete mode 100644 git-rebase--interactive.sh

diff --git a/.gitignore b/.gitignore
index 404c9a8472..3284a1e9b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -118,7 +118,6 @@
 /git-rebase--am
 /git-rebase--helper
 /git-rebase--interactive
-/git-rebase--interactive2
 /git-rebase--merge
 /git-rebase--preserve-merges
 /git-receive-pack
diff --git a/Makefile b/Makefile
index 71f8f45fe5..584834726d 100644
--- a/Makefile
+++ b/Makefile
@@ -619,7 +619,6 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--am
-SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-rebase--preserve-merges
 SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
@@ -1060,8 +1059,8 @@ BUILTIN_OBJS += builtin/prune.o
 BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/read-tree.o
-BUILTIN_OBJS += builtin/rebase--interactive2.o
 BUILTIN_OBJS += builtin/rebase--helper.o
+BUILTIN_OBJS += builtin/rebase--interactive.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
 BUILTIN_OBJS += builtin/remote.o
@@ -2400,7 +2399,6 @@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
-LOCALIZED_SH += git-rebase--interactive.sh
 LOCALIZED_SH += git-rebase--preserve-merges.sh
 LOCALIZED_SH += git-sh-setup.sh
 LOCALIZED_PERL = $(SCRIPT_PERL)
diff --git a/builtin/rebase--interactive2.c b/builtin/rebase--interactive.c
similarity index 100%
rename from builtin/rebase--interactive2.c
rename to builtin/rebase--interactive.c
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
deleted file mode 100644
index e87d708a4d..00
--- a/git-rebase--interactive.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-# This shell script fragment is sourced by git-rebase to implement
-# its interactive mode.  "git rebase --interactive" makes it easy
-# to fix up commits in the middle of a series and rearrange commits.
-#
-# Copyright (c) 2006 Johannes E. Schindelin
-#
-# The original idea comes from Eric W. Biederman, in
-# https://public-inbox.org/git/m1odwkyuf5.fsf...@ebiederm.dsl.xmission.com/
-#
-# The file containing rebase commands, comments, and empty lines.
-# This file is created by "git rebase -i" then edited by the user.  As
-# the lines are processed, they are removed from the front of this
-# file and written to the tail of $done.
-todo="$state_dir"/git-rebase-todo
-
-GIT_CHERRY_PICK_HELP="$resolvemsg"
-export GIT_CHERRY_PICK_HELP
-
-# Initiate an action. If the cannot be any
-# further action it  may exec a command
-# or exit and not return.
-#
-# TODO: Consider a cleaner return model so it
-# never exits and always return 0 if process
-# is complete.
-#
-# Parameter 1 is the action to initiate.
-#
-# Returns 0 if the action was able to complete
-# and if 1 if further processing is required.
-initiate_action () {
-   case "$1" in
-   continue)
-   exec git rebase--helper ${force_rebase:+--no-ff} 
$allow_empty_message \
---continue
-   ;;
-   skip)
-   git rerere clear
-   exec git rebase--helper ${force_rebase:+--no-ff} 
$allow_empty_message \
---continue
-   ;;
-   edit-todo)
-   exec git rebase--helper --edit-todo
-   ;;
-   show-current-patch)
-   exec git show REBASE_HEAD --
-   ;;
-   *)
-   return 1 # continue
-   ;;
-   esac
-}
-
-git_rebase__interactive () {
-   initiate_action "$action"
-   ret=$?
-   if test $ret = 0; then
-   return 0
-   fi
-
-   test -n "$keep_empty" && keep_empty="--keep-empty"
-   test -n "$rebase_merges" && rebase_merges="--rebase-merges"
-   test -n "$rebase_cousins" && rebase_cousins="--rebase-cousins"
-   test -n "$autosquash" && autosquash="--autosquash"
-   test -n "$verbose" && verbose="--verbose"
-   test -n &q

[GSoC][PATCH v7 14/20] rebase -i: rewrite the rest of init_revisions_and_shortrevisions() in C

2018-08-28 Thread Alban Gruin
This rewrites the part of init_revisions_and_shortrevisions() needed by
`--complete-action` (which initialize $shortrevisions) from shell to C.

When `upstream` is empty, it means that the user launched a `rebase
--root`, and `onto` contains the ID of an empty commit.  As a range
between an empty commit and `head` is not really meaningful, `onto` is
not used to initialize `shortrevisions` in this case.

The corresponding arguments passed to `--complete-action` are then
dropped, and init_revisions_and_shortrevisions() is stripped from
git-rebase--interactive.sh

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c   | 40 --
 git-rebase--interactive.sh | 27 -
 2 files changed, 38 insertions(+), 29 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index acc71a6f99..0716bbfd78 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -10,7 +10,7 @@ static GIT_PATH_FUNC(path_squash_onto, 
"rebase-merge/squash-onto")
 
 static int get_revision_ranges(const char *upstream, const char *onto,
   const char **head_hash,
-  char **revisions)
+  char **revisions, char **shortrevisions)
 {
const char *base_rev = upstream ? upstream : onto;
struct object_id orig_head;
@@ -19,7 +19,25 @@ static int get_revision_ranges(const char *upstream, const 
char *onto,
return error(_("no HEAD?"));
 
*head_hash = find_unique_abbrev(_head, GIT_MAX_HEXSZ);
-   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+
+   if (revisions)
+   *revisions = xstrfmt("%s...%s", base_rev, *head_hash);
+   if (shortrevisions) {
+   const char *shorthead;
+
+   shorthead = find_unique_abbrev(_head, DEFAULT_ABBREV);
+
+   if (upstream) {
+   const char *shortrev;
+   struct object_id rev_oid;
+
+   get_oid(base_rev, _oid);
+   shortrev = find_unique_abbrev(_oid, DEFAULT_ABBREV);
+
+   *shortrevisions = xstrfmt("%s..%s", shortrev, 
shorthead);
+   } else
+   *shortrevisions = xstrdup(shorthead);
+   }
 
return 0;
 }
@@ -116,7 +134,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
if (!upstream && squash_onto)
write_file(path_squash_onto(), "%s\n", squash_onto);
 
-   ret = get_revision_ranges(upstream, onto, _hash, 
);
+   ret = get_revision_ranges(upstream, onto, _hash, 
, NULL);
if (ret)
return ret;
 
@@ -145,9 +163,19 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
return !!prepare_branch_to_be_rebased(, argv[1]);
-   if (command == COMPLETE_ACTION && argc == 6)
-   return !!complete_action(, flags, argv[1], argv[2], 
argv[3],
-argv[4], argv[5], autosquash);
+   if (command == COMPLETE_ACTION && argc == 3) {
+   char *shortrevisions = NULL;
+
+   ret = get_revision_ranges(upstream, onto, _hash, NULL, 
);
+   if (ret)
+   return ret;
+
+   ret = complete_action(, flags, shortrevisions, argv[1], 
onto,
+ head_hash, argv[2], autosquash);
+
+   free(shortrevisions);
+   return !!ret;
+   }
 
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 4ca47aed1e..08e9a21c2f 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -60,23 +60,6 @@ init_basic_state () {
write_basic_state
 }
 
-init_revisions_and_shortrevisions () {
-   shorthead=$(git rev-parse --short $orig_head)
-   shortonto=$(git rev-parse --short $onto)
-   if test -z "$rebase_root"
-   # this is now equivalent to ! -z "$upstream"
-   then
-   shortupstream=$(git rev-parse --short $upstream)
-   revisions=$upstream...$orig_head
-   shortrevisions=$shortupstream..$shorthead
-   else
-   revisions=$onto...$orig_head
-   shortrevisions=$shorthead
-   test -z "$squash_onto" ||
-   echo "$squash_onto" >"$state_dir"/squash-onto
-   fi
-}
-
 git_rebase__interactive () {
initiate_action "$action"
ret=$?
@@ -87,8 +70,6 @@ git_rebase__interactive () {
git rebase--helper --prepar

[GSoC][PATCH v7 09/20] sequencer: change the way skip_unnecessary_picks() returns its result

2018-08-28 Thread Alban Gruin
Instead of skip_unnecessary_picks() printing its result to stdout, it
returns it into a struct object_id, as the rewrite of complete_action()
(to come in the next commit) will need it.

rebase--helper then is modified to fit this change.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c | 10 --
 sequencer.c  | 13 ++---
 sequencer.h  |  2 +-
 3 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 313092c465..bed3dd2b95 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -90,8 +90,14 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!transform_todos(flags);
if (command == CHECK_TODO_LIST && argc == 1)
return !!check_todo_list();
-   if (command == SKIP_UNNECESSARY_PICKS && argc == 1)
-   return !!skip_unnecessary_picks();
+   if (command == SKIP_UNNECESSARY_PICKS && argc == 1) {
+   struct object_id oid;
+   int ret = skip_unnecessary_picks();
+
+   if (!ret)
+   puts(oid_to_hex());
+   return !!ret;
+   }
if (command == REARRANGE_SQUASH && argc == 1)
return !!rearrange_squash();
if (command == ADD_EXEC && argc == 2)
diff --git a/sequencer.c b/sequencer.c
index 48447d7f0e..a56a781539 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4420,17 +4420,17 @@ static int rewrite_file(const char *path, const char 
*buf, size_t len)
 }
 
 /* skip picking commits whose parents are unchanged */
-int skip_unnecessary_picks(void)
+int skip_unnecessary_picks(struct object_id *output_oid)
 {
const char *todo_file = rebase_path_todo();
struct strbuf buf = STRBUF_INIT;
struct todo_list todo_list = TODO_LIST_INIT;
-   struct object_id onto_oid, *oid = _oid, *parent_oid;
+   struct object_id *parent_oid;
int fd, i;
 
if (!read_oneliner(, rebase_path_onto(), 0))
return error(_("could not read 'onto'"));
-   if (get_oid(buf.buf, _oid)) {
+   if (get_oid(buf.buf, output_oid)) {
strbuf_release();
return error(_("need a HEAD to fixup"));
}
@@ -4460,9 +4460,9 @@ int skip_unnecessary_picks(void)
if (item->commit->parents->next)
break; /* merge commit */
parent_oid = >commit->parents->item->object.oid;
-   if (hashcmp(parent_oid->hash, oid->hash))
+   if (hashcmp(parent_oid->hash, output_oid->hash))
break;
-   oid = >commit->object.oid;
+   oidcpy(output_oid, >commit->object.oid);
}
if (i > 0) {
int offset = get_item_line_offset(_list, i);
@@ -4491,11 +4491,10 @@ int skip_unnecessary_picks(void)
 
todo_list.current = i;
if (is_fixup(peek_command(_list, 0)))
-   record_in_rewritten(oid, peek_command(_list, 0));
+   record_in_rewritten(output_oid, 
peek_command(_list, 0));
}
 
todo_list_release(_list);
-   printf("%s\n", oid_to_hex(oid));
 
return 0;
 }
diff --git a/sequencer.h b/sequencer.h
index d2b54adf45..fcbcd246c2 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -91,7 +91,7 @@ int sequencer_add_exec_commands(const char *command);
 int transform_todos(unsigned flags);
 enum missing_commit_check_level get_missing_commit_check_level(void);
 int check_todo_list(void);
-int skip_unnecessary_picks(void);
+int skip_unnecessary_picks(struct object_id *output_oid);
 int rearrange_squash(void);
 
 extern const char sign_off_header[];
-- 
2.18.0



[GSoC][PATCH v7 07/20] rebase -i: rewrite checkout_onto() in C

2018-08-28 Thread Alban Gruin
This rewrites checkout_onto() from shell to C.

A new command (“checkout-onto”) is added to rebase--helper.c. The shell
version is then stripped.

Signed-off-by: Alban Gruin 
---
No changes since v6.

 builtin/rebase--helper.c   |  7 ++-
 git-rebase--interactive.sh | 25 -
 sequencer.c| 19 +++
 sequencer.h|  3 +++
 4 files changed, 32 insertions(+), 22 deletions(-)

diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
index 0e76dadba6..7d9426d23c 100644
--- a/builtin/rebase--helper.c
+++ b/builtin/rebase--helper.c
@@ -18,7 +18,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
enum {
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
-   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH
+   ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH,
+   CHECKOUT_ONTO
} command = 0;
struct option options[] = {
OPT_BOOL(0, "ff", _ff, N_("allow fast-forward")),
@@ -54,6 +55,8 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
EDIT_TODO),
OPT_CMDMODE(0, "prepare-branch", ,
N_("prepare the branch to be rebased"), 
PREPARE_BRANCH),
+   OPT_CMDMODE(0, "checkout-onto", ,
+   N_("checkout a commit"), CHECKOUT_ONTO),
OPT_END()
};
 
@@ -99,5 +102,7 @@ int cmd_rebase__helper(int argc, const char **argv, const 
char *prefix)
return !!edit_todo_list(flags);
if (command == PREPARE_BRANCH && argc == 2)
return !!prepare_branch_to_be_rebased(, argv[1]);
+   if (command == CHECKOUT_ONTO && argc == 4)
+   return !!checkout_onto(, argv[1], argv[2], argv[3]);
usage_with_options(builtin_rebase_helper_usage, options);
 }
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 77e972bb6c..b68f108f28 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -28,17 +28,6 @@ case "$comment_char" in
;;
 esac
 
-orig_reflog_action="$GIT_REFLOG_ACTION"
-
-comment_for_reflog () {
-   case "$orig_reflog_action" in
-   ''|rebase*)
-   GIT_REFLOG_ACTION="rebase -i ($1)"
-   export GIT_REFLOG_ACTION
-   ;;
-   esac
-}
-
 die_abort () {
apply_autostash
rm -rf "$state_dir"
@@ -70,14 +59,6 @@ collapse_todo_ids() {
git rebase--helper --shorten-ids
 }
 
-# Switch to the branch in $into and notify it in the reflog
-checkout_onto () {
-   comment_for_reflog start
-   GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
-   output git checkout $onto || die_abort "$(gettext "could not detach 
HEAD")"
-   git update-ref ORIG_HEAD $orig_head
-}
-
 get_missing_commit_check_level () {
check_level=$(git config --get rebase.missingCommitsCheck)
check_level=${check_level:-ignore}
@@ -176,7 +157,8 @@ EOF
 
git rebase--helper --check-todo-list || {
ret=$?
-   checkout_onto
+   git rebase--helper --checkout-onto "$onto_name" "$onto" \
+   "$orig_head" ${verbose:+--verbose}
exit $ret
}
 
@@ -186,7 +168,8 @@ EOF
onto="$(git rebase--helper --skip-unnecessary-picks)" ||
die "Could not skip unnecessary pick commands"
 
-   checkout_onto
+   git rebase--helper --checkout-onto "$onto_name" "$onto" "$orig_head" \
+   ${verbose:+--verbose}
require_clean_work_tree "rebase"
exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
 --continue
diff --git a/sequencer.c b/sequencer.c
index c1cfa2ee5c..48447d7f0e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3171,6 +3171,25 @@ int prepare_branch_to_be_rebased(struct replay_opts 
*opts, const char *commit)
return 0;
 }
 
+int checkout_onto(struct replay_opts *opts,
+ const char *onto_name, const char *onto,
+ const char *orig_head)
+{
+   struct object_id oid;
+   const char *action = reflog_message(opts, "start", "checkout %s", 
onto_name);
+
+   if (get_oid(orig_head, ))
+   return error(_("%s: not a valid OID"), orig_head);
+
+   if (run_git_checkout(opts, onto, action)) {
+   apply_autostash(opts);
+   sequencer_remove_state(opts);
+   return error(_("could not detach HEAD"));
+   }
+
+   return update_ref(N

  1   2   3   4   >