Merge branch 'ps/remote-rename-fix'

"git remote rename origin upstream" failed to move origin/HEAD to
upstream/HEAD when origin/HEAD is unborn and performed other
renames extremely inefficiently, which has been corrected.

* ps/remote-rename-fix:
  builtin/remote: only iterate through refs that are to be renamed
  builtin/remote: rework how remote refs get renamed
  builtin/remote: determine whether refs need renaming early on
  builtin/remote: fix sign comparison warnings
  refs: simplify logic when migrating reflog entries
  refs: pass refname when invoking reflog entry callback
main
Junio C Hamano 2025-08-21 13:46:58 -07:00
commit 9a85fa8406
19 changed files with 372 additions and 200 deletions

View File

@ -503,13 +503,12 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
}
}

static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
static int fsck_handle_reflog_ent(const char *refname,
struct object_id *ooid, struct object_id *noid,
const char *email UNUSED,
timestamp_t timestamp, int tz UNUSED,
const char *message UNUSED, void *cb_data)
const char *message UNUSED, void *cb_data UNUSED)
{
const char *refname = cb_data;

if (verbose)
fprintf_ln(stderr, _("Checking reflog %s->%s"),
oid_to_hex(ooid), oid_to_hex(noid));
@ -526,7 +525,7 @@ static int fsck_handle_reflog(const char *logname, void *cb_data)
strbuf_worktree_ref(cb_data, &refname, logname);
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
refname.buf, fsck_handle_reflog_ent,
refname.buf);
NULL);
strbuf_release(&refname);
return 0;
}

View File

@ -312,7 +312,8 @@ struct count_reflog_entries_data {
size_t limit;
};

static int count_reflog_entries(struct object_id *old_oid, struct object_id *new_oid,
static int count_reflog_entries(const char *refname UNUSED,
struct object_id *old_oid, struct object_id *new_oid,
const char *committer, timestamp_t timestamp,
int tz, const char *msg, void *cb_data)
{

View File

@ -1,9 +1,11 @@
#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS

#include "builtin.h"
#include "advice.h"
#include "config.h"
#include "date.h"
#include "gettext.h"
#include "ident.h"
#include "parse-options.h"
#include "path.h"
#include "transport.h"
@ -182,7 +184,6 @@ static int add(int argc, const char **argv, const char *prefix,
struct remote *remote;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
const char *name, *url;
int i;
int result = 0;

struct option options[] = {
@ -233,7 +234,7 @@ static int add(int argc, const char **argv, const char *prefix,
strbuf_addf(&buf, "remote.%s.fetch", name);
if (track.nr == 0)
string_list_append(&track, "*");
for (i = 0; i < track.nr; i++) {
for (size_t i = 0; i < track.nr; i++) {
add_branch(buf.buf, track.items[i].string,
name, mirror, &buf2);
}
@ -612,53 +613,169 @@ static int add_branch_for_removal(const char *refname,
struct rename_info {
const char *old_name;
const char *new_name;
struct string_list *remote_branches;
uint32_t symrefs_nr;
struct ref_transaction *transaction;
struct progress *progress;
struct strbuf *err;
uint32_t progress_nr;
uint64_t index;
};

static int read_remote_branches(const char *refname, const char *referent UNUSED,
const struct object_id *oid UNUSED,
int flags UNUSED, void *cb_data)
static void compute_renamed_ref(struct rename_info *rename,
const char *refname,
struct strbuf *out)
{
strbuf_reset(out);
strbuf_addstr(out, refname);
strbuf_splice(out, strlen("refs/remotes/"), strlen(rename->old_name),
rename->new_name, strlen(rename->new_name));
}

static int rename_one_reflog_entry(const char *old_refname,
struct object_id *old_oid,
struct object_id *new_oid,
const char *committer,
timestamp_t timestamp, int tz,
const char *msg, void *cb_data)
{
struct rename_info *rename = cb_data;
struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;
int flag;
const char *symref;
struct strbuf new_refname = STRBUF_INIT;
struct strbuf identity = STRBUF_INIT;
struct strbuf name = STRBUF_INIT;
struct strbuf mail = STRBUF_INIT;
struct ident_split ident;
const char *date;
int error;

strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
if (starts_with(refname, buf.buf)) {
item = string_list_append(rename->remote_branches, refname);
symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
refname, RESOLVE_REF_READING,
NULL, &flag);
if (symref && (flag & REF_ISSYMREF)) {
item->util = xstrdup(symref);
rename->symrefs_nr++;
} else {
item->util = NULL;
}
compute_renamed_ref(rename, old_refname, &new_refname);

if (split_ident_line(&ident, committer, strlen(committer)) < 0) {
error = -1;
goto out;
}
strbuf_release(&buf);

return 0;
strbuf_add(&name, ident.name_begin, ident.name_end - ident.name_begin);
strbuf_add(&mail, ident.mail_begin, ident.mail_end - ident.mail_begin);

date = show_date(timestamp, tz, DATE_MODE(NORMAL));
strbuf_addstr(&identity, fmt_ident(name.buf, mail.buf,
WANT_BLANK_IDENT, date, 0));

error = ref_transaction_update_reflog(rename->transaction, new_refname.buf,
new_oid, old_oid, identity.buf, msg,
rename->index++, rename->err);

out:
strbuf_release(&new_refname);
strbuf_release(&identity);
strbuf_release(&name);
strbuf_release(&mail);
return error;
}

static int rename_one_reflog(const char *old_refname,
const struct object_id *old_oid,
struct rename_info *rename)
{
struct strbuf new_refname = STRBUF_INIT;
struct strbuf message = STRBUF_INIT;
int error;

if (!refs_reflog_exists(get_main_ref_store(the_repository), old_refname))
return 0;

error = refs_for_each_reflog_ent(get_main_ref_store(the_repository),
old_refname, rename_one_reflog_entry, rename);
if (error < 0)
goto out;

compute_renamed_ref(rename, old_refname, &new_refname);

/*
* Manually write the reflog entry for the now-renamed ref. We cannot
* rely on `rename_one_ref()` to do this for us as that would screw
* over order in which reflog entries are being written.
*
* Furthermore, we only append the entry in case the reference
* resolves. Missing references shouldn't have reflogs anyway.
*/
strbuf_addf(&message, "remote: renamed %s to %s", old_refname,
new_refname.buf);

error = ref_transaction_update_reflog(rename->transaction, new_refname.buf,
old_oid, old_oid, git_committer_info(0),
message.buf, rename->index++, rename->err);
if (error < 0)
return error;

out:
strbuf_release(&new_refname);
strbuf_release(&message);
return error;
}

static int rename_one_ref(const char *old_refname, const char *referent,
const struct object_id *oid,
int flags, void *cb_data)
{
struct strbuf new_referent = STRBUF_INIT;
struct strbuf new_refname = STRBUF_INIT;
struct rename_info *rename = cb_data;
int error;

compute_renamed_ref(rename, old_refname, &new_refname);

if (flags & REF_ISSYMREF) {
/*
* Stupidly enough `referent` is not pointing to the immediate
* target of a symref, but it's the recursively resolved value.
* So symrefs pointing to symrefs would be misresolved, and
* unborn symrefs don't have any value for the `referent` at all.
*/
referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
old_refname, RESOLVE_REF_NO_RECURSE,
NULL, NULL);
compute_renamed_ref(rename, referent, &new_referent);
oid = NULL;
}

error = ref_transaction_delete(rename->transaction, old_refname,
oid, referent, REF_NO_DEREF, NULL, rename->err);
if (error < 0)
goto out;

error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo),
(flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL,
REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION,
NULL, rename->err);
if (error < 0)
goto out;

error = rename_one_reflog(old_refname, oid, rename);
if (error < 0)
goto out;

display_progress(rename->progress, ++rename->progress_nr);

out:
strbuf_release(&new_referent);
strbuf_release(&new_refname);
return error;
}

static int migrate_file(struct remote *remote)
{
struct strbuf buf = STRBUF_INIT;
int i;

strbuf_addf(&buf, "remote.%s.url", remote->name);
for (i = 0; i < remote->url.nr; i++)
for (size_t i = 0; i < remote->url.nr; i++)
repo_config_set_multivar(the_repository, buf.buf, remote->url.v[i], "^$", 0);
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.push", remote->name);
for (i = 0; i < remote->push.nr; i++)
for (int i = 0; i < remote->push.nr; i++)
repo_config_set_multivar(the_repository, buf.buf, remote->push.items[i].raw, "^$", 0);
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.fetch", remote->name);
for (i = 0; i < remote->fetch.nr; i++)
for (int i = 0; i < remote->fetch.nr; i++)
repo_config_set_multivar(the_repository, buf.buf, remote->fetch.items[i].raw, "^$", 0);
#ifndef WITH_BREAKING_CHANGES
if (remote->origin == REMOTE_REMOTES)
@ -730,6 +847,14 @@ static void handle_push_default(const char* old_name, const char* new_name)
strbuf_release(&push_default.origin);
}

static const char conflicting_remote_refs_advice[] = N_(
"The remote you are trying to rename has conflicting references in the\n"
"new target refspec. This is most likely caused by you trying to nest\n"
"a remote into itself, e.g. by renaming 'parent' into 'parent/child'\n"
"or by unnesting a remote, e.g. the other way round.\n"
"\n"
"If that is the case, you can address this by first renaming the\n"
"remote to a different name.\n");

static int mv(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
@ -741,11 +866,11 @@ static int mv(int argc, const char **argv, const char *prefix,
};
struct remote *oldremote, *newremote;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT,
old_remote_context = STRBUF_INIT;
struct string_list remote_branches = STRING_LIST_INIT_DUP;
struct rename_info rename;
int i, refs_renamed_nr = 0, refspec_updated = 0;
struct progress *progress = NULL;
old_remote_context = STRBUF_INIT, err = STRBUF_INIT;
struct rename_info rename = {
.err = &err,
};
int refspecs_need_update = 0;
int result = 0;

argc = parse_options(argc, argv, prefix, options,
@ -756,8 +881,6 @@ static int mv(int argc, const char **argv, const char *prefix,

rename.old_name = argv[0];
rename.new_name = argv[1];
rename.remote_branches = &remote_branches;
rename.symrefs_nr = 0;

oldremote = remote_get(rename.old_name);
if (!remote_is_configured(oldremote, 1)) {
@ -785,19 +908,50 @@ static int mv(int argc, const char **argv, const char *prefix,
goto out;
}

strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);

for (int i = 0; i < oldremote->fetch.nr && !refspecs_need_update; i++)
refspecs_need_update = !!strstr(oldremote->fetch.items[i].raw,
old_remote_context.buf);

if (refspecs_need_update) {
rename.transaction = ref_store_transaction_begin(get_main_ref_store(the_repository),
0, &err);
if (!rename.transaction)
goto out;

if (show_progress)
rename.progress = start_delayed_progress(the_repository,
_("Renaming remote references"), 0);

strbuf_reset(&buf);
strbuf_addf(&buf, "refs/remotes/%s/", rename.old_name);

result = refs_for_each_rawref_in(get_main_ref_store(the_repository), buf.buf,
rename_one_ref, &rename);
if (result < 0)
die(_("queueing remote ref renames failed: %s"), rename.err->buf);

result = ref_transaction_prepare(rename.transaction, &err);
if (result < 0) {
error("renaming remote references failed: %s", err.buf);
if (result == REF_TRANSACTION_ERROR_NAME_CONFLICT)
advise(conflicting_remote_refs_advice);
die(NULL);
}
}

if (oldremote->fetch.nr) {
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
repo_config_set_multivar(the_repository, buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE);
strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
for (i = 0; i < oldremote->fetch.nr; i++) {
for (int i = 0; i < oldremote->fetch.nr; i++) {
char *ptr;

strbuf_reset(&buf2);
strbuf_addstr(&buf2, oldremote->fetch.items[i].raw);
ptr = strstr(buf2.buf, old_remote_context.buf);
if (ptr) {
refspec_updated = 1;
strbuf_splice(&buf2,
ptr-buf2.buf + strlen(":refs/remotes/"),
strlen(rename.old_name), rename.new_name,
@ -813,7 +967,7 @@ static int mv(int argc, const char **argv, const char *prefix,
}

read_branches();
for (i = 0; i < branch_list.nr; i++) {
for (size_t i = 0; i < branch_list.nr; i++) {
struct string_list_item *item = branch_list.items + i;
struct branch_info *info = item->util;
if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) {
@ -828,83 +982,23 @@ static int mv(int argc, const char **argv, const char *prefix,
}
}

if (!refspec_updated)
goto out;
if (refspecs_need_update) {
result = ref_transaction_commit(rename.transaction, &err);
if (result < 0)
die(_("renaming remote refs failed: %s"), rename.err->buf);

/*
* First remove symrefs, then rename the rest, finally create
* the new symrefs.
*/
refs_for_each_ref(get_main_ref_store(the_repository),
read_remote_branches, &rename);
if (show_progress) {
/*
* Count symrefs twice, since "renaming" them is done by
* deleting and recreating them in two separate passes.
*/
progress = start_progress(the_repository,
_("Renaming remote references"),
rename.remote_branches->nr + rename.symrefs_nr);
stop_progress(&rename.progress);

handle_push_default(rename.old_name, rename.new_name);
}
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;
struct strbuf referent = STRBUF_INIT;

if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string,
&referent))
continue;
if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF))
die(_("deleting '%s' failed"), item->string);

strbuf_release(&referent);
display_progress(progress, ++refs_renamed_nr);
}
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;

if (item->util)
continue;
strbuf_reset(&buf);
strbuf_addstr(&buf, item->string);
strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
rename.new_name, strlen(rename.new_name));
strbuf_reset(&buf2);
strbuf_addf(&buf2, "remote: renamed %s to %s",
item->string, buf.buf);
if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf))
die(_("renaming '%s' failed"), item->string);
display_progress(progress, ++refs_renamed_nr);
}
for (i = 0; i < remote_branches.nr; i++) {
struct string_list_item *item = remote_branches.items + i;

if (!item->util)
continue;
strbuf_reset(&buf);
strbuf_addstr(&buf, item->string);
strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
rename.new_name, strlen(rename.new_name));
strbuf_reset(&buf2);
strbuf_addstr(&buf2, item->util);
strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name),
rename.new_name, strlen(rename.new_name));
strbuf_reset(&buf3);
strbuf_addf(&buf3, "remote: renamed %s to %s",
item->string, buf.buf);
if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf))
die(_("creating '%s' failed"), buf.buf);
display_progress(progress, ++refs_renamed_nr);
}
stop_progress(&progress);

handle_push_default(rename.old_name, rename.new_name);

out:
string_list_clear(&remote_branches, 1);
ref_transaction_free(rename.transaction);
strbuf_release(&old_remote_context);
strbuf_release(&buf);
strbuf_release(&buf2);
strbuf_release(&buf3);
strbuf_release(&err);
return result;
}

@ -920,7 +1014,7 @@ static int rm(int argc, const char **argv, const char *prefix,
struct string_list branches = STRING_LIST_INIT_DUP;
struct string_list skipped = STRING_LIST_INIT_DUP;
struct branches_for_remote cb_data;
int i, result;
int result;

memset(&cb_data, 0, sizeof(cb_data));
cb_data.branches = &branches;
@ -942,7 +1036,7 @@ static int rm(int argc, const char **argv, const char *prefix,
for_each_remote(add_known_remote, &known_remotes);

read_branches();
for (i = 0; i < branch_list.nr; i++) {
for (size_t i = 0; i < branch_list.nr; i++) {
struct string_list_item *item = branch_list.items + i;
struct branch_info *info = item->util;
if (info->remote_name && !strcmp(info->remote_name, remote->name)) {
@ -988,7 +1082,7 @@ static int rm(int argc, const char **argv, const char *prefix,
"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
"to delete them, use:",
skipped.nr));
for (i = 0; i < skipped.nr; i++)
for (size_t i = 0; i < skipped.nr; i++)
fprintf(stderr, " git branch -d %s\n",
skipped.items[i].string);
}
@ -1166,7 +1260,6 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
struct branch_info *branch_info = item->util;
struct string_list *merge = &branch_info->merge;
int width = show_info->width + 4;
int i;

if (branch_info->rebase >= REBASE_TRUE && branch_info->merge.nr > 1) {
error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"),
@ -1192,7 +1285,7 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
} else {
printf_ln(_("merges with remote %s"), merge->items[0].string);
}
for (i = 1; i < merge->nr; i++)
for (size_t i = 1; i < merge->nr; i++)
printf(_("%-*s and with remote %s\n"), width, "",
merge->items[i].string);

@ -1277,7 +1370,6 @@ static int get_one_entry(struct remote *remote, void *priv)
struct string_list *list = priv;
struct strbuf remote_info_buf = STRBUF_INIT;
struct strvec *url;
int i;

if (remote->url.nr > 0) {
struct strbuf promisor_config = STRBUF_INIT;
@ -1294,8 +1386,7 @@ static int get_one_entry(struct remote *remote, void *priv)
} else
string_list_append(list, remote->name)->util = NULL;
url = push_url_of_remote(remote);
for (i = 0; i < url->nr; i++)
{
for (size_t i = 0; i < url->nr; i++) {
strbuf_addf(&remote_info_buf, "%s (push)", url->v[i]);
string_list_append(list, remote->name)->util =
strbuf_detach(&remote_info_buf, NULL);
@ -1312,10 +1403,8 @@ static int show_all(void)
result = for_each_remote(get_one_entry, &list);

if (!result) {
int i;

string_list_sort(&list);
for (i = 0; i < list.nr; i++) {
for (size_t i = 0; i < list.nr; i++) {
struct string_list_item *item = list.items + i;
if (verbose)
printf("%s\t%s\n", item->string,
@ -1352,7 +1441,7 @@ static int show(int argc, const char **argv, const char *prefix,
query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);

for (; argc; argc--, argv++) {
int i;
size_t i;
struct strvec *url;

get_remote_ref_states(*argv, &info.states, query_flag);
@ -1458,7 +1547,7 @@ static void report_set_head_auto(const char *remote, const char *head_name,
static int set_head(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
int i, opt_a = 0, opt_d = 0, result = 0, was_detached;
int opt_a = 0, opt_d = 0, result = 0, was_detached;
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT,
b_local_head = STRBUF_INIT;
char *head_name = NULL;
@ -1492,7 +1581,7 @@ static int set_head(int argc, const char **argv, const char *prefix,
else if (states.heads.nr > 1) {
result |= error(_("Multiple remote HEAD branches. "
"Please choose one explicitly with:"));
for (i = 0; i < states.heads.nr; i++)
for (size_t i = 0; i < states.heads.nr; i++)
fprintf(stderr, " git remote set-head %s %s\n",
argv[0], states.heads.items[i].string);
} else
@ -1717,7 +1806,7 @@ static int set_branches(int argc, const char **argv, const char *prefix,
static int get_url(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
int i, push_mode = 0, all_mode = 0;
int push_mode = 0, all_mode = 0;
const char *remotename = NULL;
struct remote *remote;
struct strvec *url;
@ -1745,7 +1834,7 @@ static int get_url(int argc, const char **argv, const char *prefix,
url = push_mode ? push_url_of_remote(remote) : &remote->url;

if (all_mode) {
for (i = 0; i < url->nr; i++)
for (size_t i = 0; i < url->nr; i++)
printf_ln("%s", url->v[i]);
} else {
printf_ln("%s", url->v[0]);
@ -1757,7 +1846,7 @@ static int get_url(int argc, const char **argv, const char *prefix,
static int set_url(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
int i, push_mode = 0, add_mode = 0, delete_mode = 0;
int push_mode = 0, add_mode = 0, delete_mode = 0;
int matches = 0, negative_matches = 0;
const char *remotename = NULL;
const char *newurl = NULL;
@ -1821,7 +1910,7 @@ static int set_url(int argc, const char **argv, const char *prefix,
if (regcomp(&old_regex, oldurl, REG_EXTENDED))
die(_("Invalid old URL pattern: %s"), oldurl);

for (i = 0; i < urlset->nr; i++)
for (size_t i = 0; i < urlset->nr; i++)
if (!regexec(&old_regex, urlset->v[i], 0, NULL, 0))
matches++;
else

View File

@ -738,7 +738,8 @@ cleanup:
return ret;
}

static int reject_reflog_ent(struct object_id *ooid UNUSED,
static int reject_reflog_ent(const char *refname UNUSED,
struct object_id *ooid UNUSED,
struct object_id *noid UNUSED,
const char *email UNUSED,
timestamp_t timestamp UNUSED,
@ -2207,7 +2208,8 @@ struct stash_entry_data {
size_t count;
};

static int collect_stash_entries(struct object_id *old_oid UNUSED,
static int collect_stash_entries(const char *refname UNUSED,
struct object_id *old_oid UNUSED,
struct object_id *new_oid,
const char *committer UNUSED,
timestamp_t timestamp UNUSED,

View File

@ -1039,7 +1039,8 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
commit->object.flags |= TMP_MARK;
}

static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
static int collect_one_reflog_ent(const char *refname UNUSED,
struct object_id *ooid, struct object_id *noid,
const char *ident UNUSED,
timestamp_t timestamp UNUSED, int tz UNUSED,
const char *message UNUSED, void *cbdata)

View File

@ -1525,7 +1525,8 @@ struct grab_nth_branch_switch_cbdata {
struct strbuf *sb;
};

static int grab_nth_branch_switch(struct object_id *ooid UNUSED,
static int grab_nth_branch_switch(const char *refname UNUSED,
struct object_id *ooid UNUSED,
struct object_id *noid UNUSED,
const char *email UNUSED,
timestamp_t timestamp UNUSED,

View File

@ -22,9 +22,10 @@ struct complete_reflogs {
int nr, alloc;
};

static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
static int read_one_reflog(const char *refname UNUSED,
struct object_id *ooid, struct object_id *noid,
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct complete_reflogs *array = cb_data;
struct reflog_info *item;

View File

@ -507,7 +507,8 @@ void reflog_expiry_cleanup(void *cb_data)
free_commit_list(cb->mark_list);
}

int count_reflog_ent(struct object_id *ooid UNUSED,
int count_reflog_ent(const char *refname UNUSED,
struct object_id *ooid UNUSED,
struct object_id *noid UNUSED,
const char *email UNUSED,
timestamp_t timestamp, int tz UNUSED,

View File

@ -65,7 +65,8 @@ void reflog_expiry_prepare(const char *refname, const struct object_id *oid,
int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data);
int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
int count_reflog_ent(const char *refname,
struct object_id *ooid, struct object_id *noid,
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data);
int should_expire_reflog_ent_verbose(struct object_id *ooid,

64
refs.c
View File

@ -1022,7 +1022,6 @@ int is_branch(const char *refname)
}

struct read_ref_at_cb {
const char *refname;
timestamp_t at_time;
int cnt;
int reccnt;
@ -1052,7 +1051,8 @@ static void set_read_ref_cutoffs(struct read_ref_at_cb *cb,
*cb->cutoff_cnt = cb->reccnt;
}

static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
static int read_ref_at_ent(const char *refname,
struct object_id *ooid, struct object_id *noid,
const char *email UNUSED,
timestamp_t timestamp, int tz,
const char *message, void *cb_data)
@ -1072,14 +1072,13 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
oidcpy(cb->oid, noid);
if (!oideq(&cb->ooid, noid))
warning(_("log for ref %s has gap after %s"),
cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
}
else if (cb->date == cb->at_time)
oidcpy(cb->oid, noid);
else if (!oideq(noid, cb->oid))
warning(_("log for ref %s unexpectedly ended on %s"),
cb->refname, show_date(cb->date, cb->tz,
DATE_MODE(RFC2822)));
refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
cb->reccnt++;
oidcpy(&cb->ooid, ooid);
oidcpy(&cb->noid, noid);
@ -1094,7 +1093,8 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
return 0;
}

static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
static int read_ref_at_ent_oldest(const char *refname UNUSED,
struct object_id *ooid, struct object_id *noid,
const char *email UNUSED,
timestamp_t timestamp, int tz,
const char *message, void *cb_data)
@ -1117,7 +1117,6 @@ int read_ref_at(struct ref_store *refs, const char *refname,
struct read_ref_at_cb cb;

memset(&cb, 0, sizeof(cb));
cb.refname = refname;
cb.at_time = at_time;
cb.cnt = cnt;
cb.msg = msg;
@ -1840,7 +1839,13 @@ int refs_for_each_namespaced_ref(struct ref_store *refs,

int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(refs, "", NULL, fn, 0,
return refs_for_each_rawref_in(refs, "", fn, cb_data);
}

int refs_for_each_rawref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data)
{
return do_for_each_ref(refs, prefix, NULL, fn, 0,
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}

@ -2942,6 +2947,7 @@ struct migration_data {
struct ref_transaction *transaction;
struct strbuf *errbuf;
struct strbuf sb, name, mail;
uint64_t index;
};

static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
@ -2974,22 +2980,14 @@ done:
return ret;
}

struct reflog_migration_data {
uint64_t index;
const char *refname;
struct ref_store *old_refs;
struct ref_transaction *transaction;
struct strbuf *errbuf;
struct strbuf *sb, *name, *mail;
};

static int migrate_one_reflog_entry(struct object_id *old_oid,
static int migrate_one_reflog_entry(const char *refname,
struct object_id *old_oid,
struct object_id *new_oid,
const char *committer,
timestamp_t timestamp, int tz,
const char *msg, void *cb_data)
{
struct reflog_migration_data *data = cb_data;
struct migration_data *data = cb_data;
struct ident_split ident;
const char *date;
int ret;
@ -2997,17 +2995,17 @@ static int migrate_one_reflog_entry(struct object_id *old_oid,
if (split_ident_line(&ident, committer, strlen(committer)) < 0)
return -1;

strbuf_reset(data->name);
strbuf_add(data->name, ident.name_begin, ident.name_end - ident.name_begin);
strbuf_reset(data->mail);
strbuf_add(data->mail, ident.mail_begin, ident.mail_end - ident.mail_begin);
strbuf_reset(&data->name);
strbuf_add(&data->name, ident.name_begin, ident.name_end - ident.name_begin);
strbuf_reset(&data->mail);
strbuf_add(&data->mail, ident.mail_begin, ident.mail_end - ident.mail_begin);

date = show_date(timestamp, tz, DATE_MODE(NORMAL));
strbuf_reset(data->sb);
strbuf_addstr(data->sb, fmt_ident(data->name->buf, data->mail->buf, WANT_BLANK_IDENT, date, 0));
strbuf_reset(&data->sb);
strbuf_addstr(&data->sb, fmt_ident(data->name.buf, data->mail.buf, WANT_BLANK_IDENT, date, 0));

ret = ref_transaction_update_reflog(data->transaction, data->refname,
new_oid, old_oid, data->sb->buf,
ret = ref_transaction_update_reflog(data->transaction, refname,
new_oid, old_oid, data->sb.buf,
msg, data->index++, data->errbuf);
return ret;
}
@ -3015,18 +3013,8 @@ static int migrate_one_reflog_entry(struct object_id *old_oid,
static int migrate_one_reflog(const char *refname, void *cb_data)
{
struct migration_data *migration_data = cb_data;
struct reflog_migration_data data = {
.refname = refname,
.old_refs = migration_data->old_refs,
.transaction = migration_data->transaction,
.errbuf = migration_data->errbuf,
.sb = &migration_data->sb,
.name = &migration_data->name,
.mail = &migration_data->mail,
};

return refs_for_each_reflog_ent(migration_data->old_refs, refname,
migrate_one_reflog_entry, &data);
migrate_one_reflog_entry, migration_data);
}

static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)

13
refs.h
View File

@ -428,6 +428,8 @@ int refs_for_each_namespaced_ref(struct ref_store *refs,

/* can be used to learn about broken ref and symref */
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
int refs_for_each_rawref_in(struct ref_store *refs, const char *prefix,
each_ref_fn fn, void *cb_data);

/*
* Iterates over all refs including root refs, i.e. pseudorefs and HEAD.
@ -558,10 +560,13 @@ int refs_delete_reflog(struct ref_store *refs, const char *refname);
* The cb_data is a caller-supplied pointer given to the iterator
* functions.
*/
typedef int each_reflog_ent_fn(
struct object_id *old_oid, struct object_id *new_oid,
const char *committer, timestamp_t timestamp,
int tz, const char *msg, void *cb_data);
typedef int each_reflog_ent_fn(const char *refname,
struct object_id *old_oid,
struct object_id *new_oid,
const char *committer,
timestamp_t timestamp,
int tz, const char *msg,
void *cb_data);

/* Iterate over reflog entries in the log for `refname`. */


View File

@ -277,7 +277,8 @@ struct debug_reflog {
void *cb_data;
};

static int debug_print_reflog_ent(struct object_id *old_oid,
static int debug_print_reflog_ent(const char *refname,
struct object_id *old_oid,
struct object_id *new_oid,
const char *committer, timestamp_t timestamp,
int tz, const char *msg, void *cb_data)
@ -292,7 +293,7 @@ static int debug_print_reflog_ent(struct object_id *old_oid,
if (new_oid)
oid_to_hex_r(n, new_oid);

ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg,
ret = dbg->fn(refname, old_oid, new_oid, committer, timestamp, tz, msg,
dbg->cb_data);
trace_printf_key(&trace_refs,
"reflog_ent %s (ret %d): %s -> %s, %s %ld \"%.*s\"\n",

View File

@ -2115,7 +2115,9 @@ static int files_delete_reflog(struct ref_store *ref_store,
return ret;
}

static int show_one_reflog_ent(struct files_ref_store *refs, struct strbuf *sb,
static int show_one_reflog_ent(struct files_ref_store *refs,
const char *refname,
struct strbuf *sb,
each_reflog_ent_fn fn, void *cb_data)
{
struct object_id ooid, noid;
@ -2142,7 +2144,7 @@ static int show_one_reflog_ent(struct files_ref_store *refs, struct strbuf *sb,
message += 6;
else
message += 7;
return fn(&ooid, &noid, p, timestamp, tz, message, cb_data);
return fn(refname, &ooid, &noid, p, timestamp, tz, message, cb_data);
}

static char *find_beginning_of_line(char *bob, char *scan)
@ -2226,7 +2228,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
scanp = bp;
endp = bp + 1;
ret = show_one_reflog_ent(refs, &sb, fn, cb_data);
ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data);
strbuf_reset(&sb);
if (ret)
break;
@ -2238,7 +2240,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
* Process it, and we can end the loop.
*/
strbuf_splice(&sb, 0, 0, buf, endp - buf);
ret = show_one_reflog_ent(refs, &sb, fn, cb_data);
ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data);
strbuf_reset(&sb);
break;
}
@ -2288,7 +2290,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
return -1;

while (!ret && !strbuf_getwholeline(&sb, logfp, '\n'))
ret = show_one_reflog_ent(refs, &sb, fn, cb_data);
ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data);
fclose(logfp);
strbuf_release(&sb);
return ret;
@ -3360,7 +3362,8 @@ struct expire_reflog_cb {
dry_run:1;
};

static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
static int expire_reflog_ent(const char *refname UNUSED,
struct object_id *ooid, struct object_id *noid,
const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{

View File

@ -2155,7 +2155,7 @@ static int yield_log_record(struct reftable_ref_store *refs,

full_committer = fmt_ident(log->value.update.name, log->value.update.email,
WANT_COMMITTER_IDENT, NULL, IDENT_NO_DATE);
return fn(&old_oid, &new_oid, full_committer,
return fn(log->refname, &old_oid, &new_oid, full_committer,
log->value.update.time, log->value.update.tz_offset,
log->value.update.message, cb_data);
}

View File

@ -2578,7 +2578,8 @@ struct check_and_collect_until_cb_data {
};

/* Get the timestamp of the latest entry. */
static int peek_reflog(struct object_id *o_oid UNUSED,
static int peek_reflog(const char *refname UNUSED,
struct object_id *o_oid UNUSED,
struct object_id *n_oid UNUSED,
const char *ident UNUSED,
timestamp_t timestamp, int tz UNUSED,
@ -2589,7 +2590,8 @@ static int peek_reflog(struct object_id *o_oid UNUSED,
return 1;
}

static int check_and_collect_until(struct object_id *o_oid UNUSED,
static int check_and_collect_until(const char *refname UNUSED,
struct object_id *o_oid UNUSED,
struct object_id *n_oid,
const char *ident UNUSED,
timestamp_t timestamp, int tz UNUSED,

View File

@ -1689,7 +1689,8 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
}
}

static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
static int handle_one_reflog_ent(const char *refname UNUSED,
struct object_id *ooid, struct object_id *noid,
const char *email UNUSED,
timestamp_t timestamp UNUSED,
int tz UNUSED,

View File

@ -215,7 +215,8 @@ static int cmd_for_each_reflog(struct ref_store *refs,
return refs_for_each_reflog(refs, each_reflog, NULL);
}

static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid,
static int each_reflog_ent(const char *refname UNUSED,
struct object_id *old_oid, struct object_id *new_oid,
const char *committer, timestamp_t timestamp,
int tz, const char *msg, void *cb_data UNUSED)
{

View File

@ -1658,4 +1658,77 @@ test_expect_success 'forbid adding superset of existing remote' '
test_grep ".outer. is a superset of existing remote .outer/inner." err
'

test_expect_success 'rename handles unborn HEAD' '
test_when_finished "git remote remove unborn-renamed" &&
git remote add unborn url &&
git symbolic-ref refs/remotes/unborn/HEAD refs/remotes/unborn/nonexistent &&
git remote rename unborn unborn-renamed &&
git symbolic-ref refs/remotes/unborn-renamed/HEAD >actual &&
echo refs/remotes/unborn-renamed/nonexistent >expected &&
test_cmp expected actual
'

test_expect_success 'rename can nest a remote into itself' '
test_commit parent-commit &&
COMMIT_ID=$(git rev-parse HEAD) &&
test_when_finished "git remote remove parent || true" &&
git remote add parent url &&
git update-ref refs/remotes/parent/branch $COMMIT_ID &&
test_when_finished "git remote remove parent/child" &&
git remote rename parent parent/child &&
git for-each-ref refs/remotes/ >actual &&
printf "$COMMIT_ID commit\trefs/remotes/parent/child/branch\n" >expected &&
test_cmp expected actual
'

test_expect_success 'rename can nest a remote into itself with a conflicting branch name' '
test_commit parent-conflict &&
COMMIT_ID=$(git rev-parse HEAD) &&
test_when_finished "git remote remove parent || true" &&
git remote add parent url &&
git update-ref refs/remotes/parent/child $COMMIT_ID &&
test_when_finished "git remote remove parent/child" &&
test_must_fail git remote rename parent parent/child 2>err &&
test_grep "renaming remote references failed" err &&
test_grep "The remote you are trying to rename has conflicting references" err &&
git for-each-ref refs/remotes/ >actual &&
printf "$COMMIT_ID commit\trefs/remotes/parent/child\n" >expected &&
test_cmp expected actual
'

test_expect_success 'rename can unnest a remote' '
test_commit parent-child-commit &&
COMMIT_ID=$(git rev-parse HEAD) &&
test_when_finished "git remote remove parent/child || true" &&
git remote add parent/child url &&
git update-ref refs/remotes/parent/child/branch $COMMIT_ID &&
git remote rename parent/child parent &&
git for-each-ref refs/remotes/ >actual &&
printf "$COMMIT_ID commit\trefs/remotes/parent/branch\n" >expected &&
test_cmp expected actual
'

test_expect_success 'rename moves around the reflog' '
test_commit reflog-old &&
COMMIT_ID=$(git rev-parse HEAD) &&
test_config core.logAllRefUpdates true &&
test_when_finished "git remote remove reflog-old || true" &&
git remote add reflog-old url &&
git update-ref refs/remotes/reflog-old/branch $COMMIT_ID &&
test-tool ref-store main for-each-reflog >actual &&
test_grep refs/remotes/reflog-old/branch actual &&
test-tool ref-store main for-each-reflog-ent refs/remotes/reflog-old/branch >reflog-entries-old &&
test_line_count = 1 reflog-entries-old &&
git remote rename reflog-old reflog-new &&
test-tool ref-store main for-each-reflog >actual &&
test_grep ! refs/remotes/reflog-old actual &&
test_grep refs/remotes/reflog-new/branch actual &&
test-tool ref-store main for-each-reflog-ent refs/remotes/reflog-new/branch >reflog-entries-new &&
cat >expect <<-EOF &&
$(cat reflog-entries-old)
$COMMIT_ID $COMMIT_ID $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1112912173 -0700 remote: renamed refs/remotes/reflog-old/branch to refs/remotes/reflog-new/branch
EOF
test_cmp expect reflog-entries-new
'

test_done

View File

@ -972,7 +972,8 @@ static void wt_longstatus_print_changed(struct wt_status *s)
wt_longstatus_print_trailer(s);
}

static int stash_count_refs(struct object_id *ooid UNUSED,
static int stash_count_refs(const char *refname UNUSED,
struct object_id *ooid UNUSED,
struct object_id *noid UNUSED,
const char *email UNUSED,
timestamp_t timestamp UNUSED, int tz UNUSED,
@ -1664,7 +1665,8 @@ struct grab_1st_switch_cbdata {
struct object_id noid;
};

static int grab_1st_switch(struct object_id *ooid UNUSED,
static int grab_1st_switch(const char *refname UNUSED,
struct object_id *ooid UNUSED,
struct object_id *noid,
const char *email UNUSED,
timestamp_t timestamp UNUSED, int tz UNUSED,