Skip to content

Commit 48a72f6

Browse files
pks-tgitster
authored andcommitted
replay: support updating detached HEAD
In a subsequent commit we're about to introduce a new git-history(1) command, which will by default work on all local branches and HEAD. This is already well-supported by the replay machinery for most of the part: updating branches is one of its prime use cases, and the HEAD ref is also updated in case it points to any of the branches. However, what's not supported yet is to update HEAD in case it is not a symbolic ref. We determine the refs that need to be updated by iterating through the decorations of the original commit, but we only update those refs that are `DECORATION_REF_LOCAL`, which covers local branches. Address this gap by also handling `DECORATION_REF_HEAD`. Note though that this needs to only happen in case we're working on a detached HEAD. If HEAD is pointing to a branch, then we'd already update that branch via `DECORATION_REF_LOCAL`. Refactor the loop that iterates through the decorations a bit to make the individual conditions easier to understand. Based-on-patch-by: Elijah Newren <newren@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 5425771 commit 48a72f6

2 files changed

Lines changed: 42 additions & 14 deletions

File tree

replay.c

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,17 @@ static void get_ref_information(struct repository *repo,
150150
static void set_up_replay_mode(struct repository *repo,
151151
struct rev_cmdline_info *cmd_info,
152152
const char *onto_name,
153+
bool *detached_head,
153154
char **advance_name,
154155
struct commit **onto,
155156
struct strset **update_refs)
156157
{
157158
struct ref_info rinfo;
159+
int head_flags = 0;
160+
161+
refs_read_ref_full(get_main_ref_store(repo), "HEAD",
162+
RESOLVE_REF_NO_RECURSE, NULL, &head_flags);
163+
*detached_head = !(head_flags & REF_ISSYMREF);
158164

159165
get_ref_information(repo, cmd_info, &rinfo);
160166
if (!rinfo.positive_refexprs)
@@ -269,12 +275,13 @@ int replay_revisions(struct rev_info *revs,
269275
struct merge_result result = {
270276
.clean = 1,
271277
};
278+
bool detached_head;
272279
char *advance;
273280
int ret;
274281

275282
advance = xstrdup_or_null(opts->advance);
276-
set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto, &advance,
277-
&onto, &update_refs);
283+
set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto,
284+
&detached_head, &advance, &onto, &update_refs);
278285

279286
/* FIXME: Should allow replaying commits with the first as a root commit */
280287

@@ -312,18 +319,30 @@ int replay_revisions(struct rev_info *revs,
312319
/* Update any necessary branches */
313320
if (advance)
314321
continue;
315-
decoration = get_name_decoration(&commit->object);
316-
if (!decoration)
317-
continue;
318-
while (decoration) {
319-
if (decoration->type == DECORATION_REF_LOCAL &&
320-
(opts->contained || strset_contains(update_refs,
321-
decoration->name))) {
322-
replay_result_queue_update(out, decoration->name,
323-
&commit->object.oid,
324-
&last_commit->object.oid);
325-
}
326-
decoration = decoration->next;
322+
323+
for (decoration = get_name_decoration(&commit->object);
324+
decoration;
325+
decoration = decoration->next)
326+
{
327+
if (decoration->type != DECORATION_REF_LOCAL &&
328+
decoration->type != DECORATION_REF_HEAD)
329+
continue;
330+
331+
/*
332+
* We only need to update HEAD separately in case it's
333+
* detached. If it's not we'd already update the branch
334+
* it is pointing to.
335+
*/
336+
if (decoration->type == DECORATION_REF_HEAD && !detached_head)
337+
continue;
338+
339+
if (!opts->contained &&
340+
!strset_contains(update_refs, decoration->name))
341+
continue;
342+
343+
replay_result_queue_update(out, decoration->name,
344+
&commit->object.oid,
345+
&last_commit->object.oid);
327346
}
328347
}
329348

t/t3650-replay-basics.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ test_expect_success 'using replay on bare repo to rebase multiple divergent bran
249249
done
250250
'
251251

252+
test_expect_success 'using replay to update detached HEAD' '
253+
current_head=$(git branch --show-current) &&
254+
test_when_finished git switch "$current_head" &&
255+
git switch --detach &&
256+
test_commit something &&
257+
git replay --ref-action=print --onto HEAD~2 --ref-action=print HEAD~..HEAD >updates &&
258+
test_grep "update HEAD " updates
259+
'
260+
252261
test_expect_success 'merge.directoryRenames=false' '
253262
# create a test case that stress-tests the rename caching
254263
git switch -c rename-onto &&

0 commit comments

Comments
 (0)