On 09/08/18 10:22, Johannes Schindelin wrote: > Hi Phillip, > > On Mon, 6 Aug 2018, Phillip Wood wrote: > >> On 06/08/18 10:52, Johannes Schindelin via GitGitGadget wrote: >>> >>> From: Johannes Schindelin <johannes.schindelin@xxxxxx> >>> >>> The idea of `--exec` is to append an `exec` call after each `pick`. >>> >>> Since the introduction of fixup!/squash! commits, this idea was extended >>> to apply to "pick, possibly followed by a fixup/squash chain", i.e. an >>> exec would not be inserted between a `pick` and any of its corresponding >>> `fixup` or `squash` lines. >>> >>> The current implementation uses a dirty trick to achieve that: it >>> assumes that there are only pick/fixup/squash commands, and then >>> *inserts* the `exec` lines before any `pick` but the first, and appends >>> a final one. >>> >>> With the todo lists generated by `git rebase --rebase-merges`, this >>> simple implementation shows its problems: it produces the exact wrong >>> thing when there are `label`, `reset` and `merge` commands. >>> >>> Let's change the implementation to do exactly what we want: look for >>> `pick` lines, skip any fixup/squash chains, and then insert the `exec` >>> line. Lather, rinse, repeat. >>> >>> While at it, also add `exec` lines after `merge` commands, because they >>> are similar in spirit to `pick` commands: they add new commits. >>> >>> Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> >>> --- >>> sequencer.c | 37 +++++++++++++++++++++++++++---------- >>> t/t3430-rebase-merges.sh | 2 +- >>> 2 files changed, 28 insertions(+), 11 deletions(-) >>> >>> diff --git a/sequencer.c b/sequencer.c >>> index 31038472f..ed2e694ff 100644 >>> --- a/sequencer.c >>> +++ b/sequencer.c >>> @@ -4244,10 +4244,9 @@ 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 = &todo_list.buf; >>> size_t offset = 0, commands_len = strlen(commands); >>> - int i, first; >>> + int i, insert; >>> >>> if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) >>> return error(_("could not read '%s'."), todo_file); >>> @@ -4257,19 +4256,37 @@ int sequencer_add_exec_commands(const char >>> *commands) >>> return error(_("unusable todo list: '%s'"), todo_file); >>> } >>> - first = 1; >>> - /* insert <commands> before every pick except the first one */ >>> - for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) { >>> - if (item->command == TODO_PICK && !first) { >>> - strbuf_insert(buf, item->offset_in_buf + offset, >>> - commands, commands_len); >>> + /* >>> + * Insert <commands> 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; >>> + >>> + if (insert >= 0) { >>> + /* skip fixup/squash chains */ >>> + if (command == TODO_COMMENT) >>> + continue; >> >> insert is not updated so if the next command is not a fixup the exec >> line will be inserted before the comment. > > Yes, this is very much on purpose. Take this todo list, for example: > > pick 123456 this patch > # pick 987654 this was an empty commit > > You definitely do not want the `exec` to appear after that commented-out > empty commit. Yes, I like it, I was just thinking out loud. >>> + else if (is_fixup(command)) { >>> + insert = i + 1; >>> + continue; >>> + } >>> + strbuf_insert(buf, >>> + todo_list.items[insert].offset_in_buf + >>> + offset, commands, commands_len); >>> offset += commands_len; >>> + insert = -1; >>> } >>> - first = 0; >>> + >>> + if (command == TODO_PICK || command == TODO_MERGE) >>> + insert = i + 1; >>> } >>> >>> /* append final <commands> */ >>> - strbuf_add(buf, commands, commands_len); >>> + if (insert >= 0 || !offset) >>> + strbuf_add(buf, commands, commands_len); >> >> Having read your other message about this patch I think if you wanted to fix >> the position of the final exec in the case where the todo list ends with a >> comment you could do something like >> >> if (insert >= 0) >> strbuf_insert(buf, >> todo_list.items[insert].offset_in_buf + >> offset, commands, commands_len); >> else >> strbuf_add(buf, commands, commands_len); > > That does not really work, as `insert` can point *after* the last line, in > which case `todo_list.items[insert]` is undefined (and in the worst case, > causes a segmentation fault). Ah, I'd missed that, does changing the conditions to if (insert >= 0 && insert < todo.list_nr) and else if (insert >=0 || !offset) work? >> I'm not sure it matters that much though > > Well, it does matter to me. After having this in the back of my head, and > after your comment, I think it *is* worth the additional complexity after > all. It would definitely be nice to have. Best Wishes Phillip > Will come up with a new iteration. > > Ciao, > Dscho >