diff options
author | Derrick Stolee <derrickstolee@github.com> | 2022-07-19 18:33:41 (GMT) |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2022-07-19 19:49:04 (GMT) |
commit | b3b1a21d1a5df87e81fa8b53c6c3cf9760ca12d4 (patch) | |
tree | b5e346ed2f5f55e5006372505ff9950272bd03d5 /sequencer.c | |
parent | 89fc0b53fdb4ea2f9581ea728577f4d2a4947672 (diff) | |
download | git-b3b1a21d1a5df87e81fa8b53c6c3cf9760ca12d4.zip git-b3b1a21d1a5df87e81fa8b53c6c3cf9760ca12d4.tar.gz git-b3b1a21d1a5df87e81fa8b53c6c3cf9760ca12d4.tar.bz2 |
sequencer: rewrite update-refs as user edits todo list
An interactive rebase provides opportunities for the user to edit the
todo list. The --update-refs option initializes the list with some
'update-ref <ref>' steps, but the user could add these manually.
Further, the user could add or remove these steps during pauses in the
interactive rebase.
Add a new method, todo_list_filter_update_refs(), that scans a todo_list
and compares it to the stored update-refs file. There are two actions
that can happen at this point:
1. If a '<ref>/<before>/<after>' triple in the update-refs file does not
have a matching 'update-ref <ref>' command in the todo-list _and_ the
<after> value is the null OID, then remove that triple. Here, the
user removed the 'update-ref <ref>' command before it was executed,
since if it was executed then the <after> value would store the
commit at that position.
2. If a 'update-ref <ref>' command in the todo-list does not have a
matching '<ref>/<before>/<after>' triple in the update-refs file,
then insert a new one. Store the <before> value to be the current
OID pointed at by <ref>. This is handled inside of the
init_update_ref_record() helper method.
We can test that this works by rewriting the todo-list several times in
the course of a rebase. Check that each ref is locked or unlocked for
updates after each todo-list update. We can also verify that the ref
update fails if a concurrent process updates one of the refs after the
rebase process records the "locked" ref location.
To help these tests, add a new 'set_replace_editor' helper that will
replace the todo-list with an exact file.
Reported-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'sequencer.c')
-rw-r--r-- | sequencer.c | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/sequencer.c b/sequencer.c index 98111e3..20ca7b4 100644 --- a/sequencer.c +++ b/sequencer.c @@ -4168,6 +4168,102 @@ cleanup: return result; } +/* + * Parse the update-refs file for the current rebase, then remove the + * refs that do not appear in the todo_list (and have not had updated + * values stored) and add refs that are in the todo_list but not + * represented in the update-refs file. + * + * If there are changes to the update-refs list, then write the new state + * to disk. + */ +void todo_list_filter_update_refs(struct repository *r, + struct todo_list *todo_list) +{ + int i; + int updated = 0; + struct string_list update_refs = STRING_LIST_INIT_DUP; + + sequencer_get_update_refs_state(r->gitdir, &update_refs); + + /* + * For each item in the update_refs list, if it has no updated + * value and does not appear in the todo_list, then remove it + * from the update_refs list. + */ + for (i = 0; i < update_refs.nr; i++) { + int j; + int found = 0; + const char *ref = update_refs.items[i].string; + size_t reflen = strlen(ref); + struct update_ref_record *rec = update_refs.items[i].util; + + /* OID already stored as updated. */ + if (!is_null_oid(&rec->after)) + continue; + + for (j = 0; !found && j < todo_list->total_nr; j++) { + struct todo_item *item = &todo_list->items[j]; + const char *arg = todo_list->buf.buf + item->arg_offset; + + if (item->command != TODO_UPDATE_REF) + continue; + + if (item->arg_len != reflen || + strncmp(arg, ref, reflen)) + continue; + + found = 1; + } + + if (!found) { + free(update_refs.items[i].string); + free(update_refs.items[i].util); + + update_refs.nr--; + MOVE_ARRAY(update_refs.items + i, update_refs.items + i + 1, update_refs.nr - i); + + updated = 1; + i--; + } + } + + /* + * For each todo_item, check if its ref is in the update_refs list. + * If not, then add it as an un-updated ref. + */ + for (i = 0; i < todo_list->total_nr; i++) { + struct todo_item *item = &todo_list->items[i]; + const char *arg = todo_list->buf.buf + item->arg_offset; + int j, found = 0; + + if (item->command != TODO_UPDATE_REF) + continue; + + for (j = 0; !found && j < update_refs.nr; j++) { + const char *ref = update_refs.items[j].string; + + found = strlen(ref) == item->arg_len && + !strncmp(ref, arg, item->arg_len); + } + + if (!found) { + struct string_list_item *inserted; + struct strbuf argref = STRBUF_INIT; + + strbuf_add(&argref, arg, item->arg_len); + inserted = string_list_insert(&update_refs, argref.buf); + inserted->util = init_update_ref_record(argref.buf); + strbuf_release(&argref); + updated = 1; + } + } + + if (updated) + write_update_refs_state(&update_refs); + string_list_clear(&update_refs, 1); +} + static int do_update_ref(struct repository *r, const char *refname) { struct string_list_item *item; |