Merge branch 'bf/set-head-symref' into js/set-head-symref-fix

* bf/set-head-symref:
  fetch set_head: handle mirrored bare repositories
  fetch: set remote/HEAD if it does not exist
  refs: add create_only option to refs_update_symref_extended
  refs: add TRANSACTION_CREATE_EXISTS error
  remote set-head: better output for --auto
  remote set-head: refactor for readability
  refs: atomically record overwritten ref in update_symref
  refs: standardize output of refs_read_symbolic_ref
  t/t5505-remote: test failure of set-head
  t/t5505-remote: set default branch to main
maint
Junio C Hamano 2024-12-06 12:09:43 +09:00
commit e02082c7f8
18 changed files with 438 additions and 58 deletions

View File

@ -1577,6 +1577,72 @@ static int backfill_tags(struct display_state *display_state,
return retcode;
}

static const char *strip_refshead(const char *name){
skip_prefix(name, "refs/heads/", &name);
return name;
}

static int set_head(const struct ref *remote_refs)
{
int result = 0, is_bare;
struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT;
const char *remote = gtransport->remote->name;
char *head_name = NULL;
struct ref *ref, *matches;
struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
struct refspec_item refspec = {
.force = 0,
.pattern = 1,
.src = (char *) "refs/heads/*",
.dst = (char *) "refs/heads/*",
};
struct string_list heads = STRING_LIST_INIT_DUP;
struct ref_store *refs = get_main_ref_store(the_repository);

get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
for (ref = matches; ref; ref = ref->next) {
string_list_append(&heads, strip_refshead(ref->name));
}


if (!heads.nr)
result = 1;
else if (heads.nr > 1)
result = 1;
else
head_name = xstrdup(heads.items[0].string);

if (!head_name)
goto cleanup;
is_bare = is_bare_repository();
if (is_bare) {
strbuf_addstr(&b_head, "HEAD");
strbuf_addf(&b_remote_head, "refs/heads/%s", head_name);
} else {
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", remote);
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", remote, head_name);
}
/* make sure it's valid */
if (!is_bare && !refs_ref_exists(refs, b_remote_head.buf)) {
result = 1;
goto cleanup;
}
if (refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
"fetch", NULL, !is_bare))
result = 1;

cleanup:
free(head_name);
free_refs(fetch_map);
free_refs(matches);
string_list_clear(&heads, 0);
strbuf_release(&b_head);
strbuf_release(&b_remote_head);
return result;
}

static int do_fetch(struct transport *transport,
struct refspec *rs,
const struct fetch_config *config)
@ -1646,6 +1712,8 @@ static int do_fetch(struct transport *transport,
"refs/tags/");
}

strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");

if (must_list_refs) {
trace2_region_enter("fetch", "remote_refs", the_repository);
remote_refs = transport_get_remote_refs(transport,
@ -1790,6 +1858,12 @@ static int do_fetch(struct transport *transport,
"you need to specify exactly one branch with the --set-upstream option"));
}
}
if (set_head(remote_refs))
;
/*
* Way too many cases where this can go wrong
* so let's just fail silently for now.
*/

cleanup:
if (retcode) {

View File

@ -1399,11 +1399,40 @@ static int show(int argc, const char **argv, const char *prefix)
return result;
}

static void report_set_head_auto(const char *remote, const char *head_name,
struct strbuf *b_local_head, int was_detached) {
struct strbuf buf_prefix = STRBUF_INIT;
const char *prev_head = NULL;

strbuf_addf(&buf_prefix, "refs/remotes/%s/", remote);
skip_prefix(b_local_head->buf, buf_prefix.buf, &prev_head);

if (prev_head && !strcmp(prev_head, head_name))
printf(_("'%s/HEAD' is unchanged and points to '%s'\n"),
remote, head_name);
else if (prev_head)
printf(_("'%s/HEAD' has changed from '%s' and now points to '%s'\n"),
remote, prev_head, head_name);
else if (!b_local_head->len)
printf(_("'%s/HEAD' is now created and points to '%s'\n"),
remote, head_name);
else if (was_detached && b_local_head->len)
printf(_("'%s/HEAD' was detached at '%s' and now points to '%s'\n"),
remote, b_local_head->buf, head_name);
else
printf(_("'%s/HEAD' used to point to '%s' "
"(which is not a remote branch), but now points to '%s'\n"),
remote, b_local_head->buf, head_name);
strbuf_release(&buf_prefix);
}

static int set_head(int argc, const char **argv, const char *prefix)
{
int i, opt_a = 0, opt_d = 0, result = 0;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
int i, 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;
struct ref_store *refs = get_main_ref_store(the_repository);

struct option options[] = {
OPT_BOOL('a', "auto", &opt_a,
@ -1415,7 +1444,7 @@ static int set_head(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options,
builtin_remote_sethead_usage, 0);
if (argc)
strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]);
strbuf_addf(&b_head, "refs/remotes/%s/HEAD", argv[0]);

if (!opt_a && !opt_d && argc == 2) {
head_name = xstrdup(argv[1]);
@ -1434,25 +1463,32 @@ static int set_head(int argc, const char **argv, const char *prefix)
head_name = xstrdup(states.heads.items[0].string);
free_remote_ref_states(&states);
} else if (opt_d && !opt_a && argc == 1) {
if (refs_delete_ref(get_main_ref_store(the_repository), NULL, buf.buf, NULL, REF_NO_DEREF))
result |= error(_("Could not delete %s"), buf.buf);
if (refs_delete_ref(refs, NULL, b_head.buf, NULL, REF_NO_DEREF))
result |= error(_("Could not delete %s"), b_head.buf);
} else
usage_with_options(builtin_remote_sethead_usage, options);

if (head_name) {
strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
/* make sure it's valid */
if (!refs_ref_exists(get_main_ref_store(the_repository), buf2.buf))
result |= error(_("Not a valid ref: %s"), buf2.buf);
else if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, "remote set-head"))
result |= error(_("Could not setup %s"), buf.buf);
else if (opt_a)
printf("%s/HEAD set to %s\n", argv[0], head_name);
free(head_name);
if (!head_name)
goto cleanup;
strbuf_addf(&b_remote_head, "refs/remotes/%s/%s", argv[0], head_name);
if (!refs_ref_exists(refs, b_remote_head.buf)) {
result |= error(_("Not a valid ref: %s"), b_remote_head.buf);
goto cleanup;
}
was_detached = refs_update_symref_extended(refs, b_head.buf, b_remote_head.buf,
"remote set-head", &b_local_head, 0);
if (was_detached == -1) {
result |= error(_("Could not set up %s"), b_head.buf);
goto cleanup;
}
if (opt_a)
report_set_head_auto(argv[0], head_name, &b_local_head, was_detached);

strbuf_release(&buf);
strbuf_release(&buf2);
cleanup:
free(head_name);
strbuf_release(&b_head);
strbuf_release(&b_remote_head);
strbuf_release(&b_local_head);
return result;
}


47
refs.c
View File

@ -2115,20 +2115,54 @@ int peel_iterated_oid(struct repository *r, const struct object_id *base, struct

int refs_update_symref(struct ref_store *refs, const char *ref,
const char *target, const char *logmsg)
{
return refs_update_symref_extended(refs, ref, target, logmsg, NULL, 0);
}

int refs_update_symref_extended(struct ref_store *refs, const char *ref,
const char *target, const char *logmsg,
struct strbuf *referent, int create_only)
{
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
int ret = 0;
int ret = 0, prepret = 0;

transaction = ref_store_transaction_begin(refs, &err);
if (!transaction ||
ref_transaction_update(transaction, ref, NULL, NULL,
target, NULL, REF_NO_DEREF,
logmsg, &err) ||
ref_transaction_commit(transaction, &err)) {
if (!transaction) {
error_return:
ret = error("%s", err.buf);
goto cleanup;
}
if (create_only) {
if (ref_transaction_create(transaction, ref, NULL, target,
REF_NO_DEREF, logmsg, &err))
goto error_return;
prepret = ref_transaction_prepare(transaction, &err);
if (prepret && prepret != TRANSACTION_CREATE_EXISTS)
goto error_return;
} else {
if (ref_transaction_update(transaction, ref, NULL, NULL,
target, NULL, REF_NO_DEREF,
logmsg, &err) ||
ref_transaction_prepare(transaction, &err))
goto error_return;
}

if (referent && refs_read_symbolic_ref(refs, ref, referent) == NOT_A_SYMREF) {
struct object_id oid;
if (!refs_read_ref(refs, ref, &oid)) {
strbuf_addstr(referent, oid_to_hex(&oid));
ret = NOT_A_SYMREF;
}
}

if (prepret == TRANSACTION_CREATE_EXISTS)
goto cleanup;

if (ref_transaction_commit(transaction, &err))
goto error_return;

cleanup:
strbuf_release(&err);
if (transaction)
ref_transaction_free(transaction);
@ -2948,4 +2982,3 @@ int ref_update_expects_existing_old_ref(struct ref_update *update)
return (update->flags & REF_HAVE_OLD) &&
(!is_null_oid(&update->old_oid) || update->old_target);
}


19
refs.h
View File

@ -83,6 +83,17 @@ int refs_read_ref_full(struct ref_store *refs, const char *refname,

int refs_read_ref(struct ref_store *refs, const char *refname, struct object_id *oid);

#define NOT_A_SYMREF -2

/*
* Read the symbolic ref named "refname" and write its immediate referent into
* the provided buffer. Referent is left empty if "refname" is not a symbolic
* ref. It does not resolve the symbolic reference recursively in case the
* target is also a symbolic ref.
*
* Returns 0 on success, -2 if the "refname" is not a symbolic ref,
* -1 otherwise.
*/
int refs_read_symbolic_ref(struct ref_store *ref_store, const char *refname,
struct strbuf *referent);

@ -573,6 +584,10 @@ int refs_copy_existing_ref(struct ref_store *refs, const char *oldref,
int refs_update_symref(struct ref_store *refs, const char *refname,
const char *target, const char *logmsg);

int refs_update_symref_extended(struct ref_store *refs, const char *refname,
const char *target, const char *logmsg,
struct strbuf *referent, int create_only);

enum action_on_err {
UPDATE_REFS_MSG_ON_ERR,
UPDATE_REFS_DIE_ON_ERR,
@ -758,8 +773,10 @@ int ref_transaction_verify(struct ref_transaction *transaction,

/* Naming conflict (for example, the ref names A and A/B conflict). */
#define TRANSACTION_NAME_CONFLICT -1
/* When only creation was requested, but the ref already exists. */
#define TRANSACTION_CREATE_EXISTS -2
/* All other errors. */
#define TRANSACTION_GENERIC_ERROR -2
#define TRANSACTION_GENERIC_ERROR -3

/*
* Perform the preparatory stages of committing `transaction`. Acquire

View File

@ -596,10 +596,9 @@ static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refn
unsigned int type;

ret = read_ref_internal(ref_store, refname, &oid, referent, &type, &failure_errno, 1);
if (ret)
return ret;

return !(type & REF_ISSYMREF);
if (!ret && !(type & REF_ISSYMREF))
return NOT_A_SYMREF;
return ret;
}

int parse_loose_ref_contents(const struct git_hash_algo *algop,
@ -2502,14 +2501,18 @@ static int split_symref_update(struct ref_update *update,
static int check_old_oid(struct ref_update *update, struct object_id *oid,
struct strbuf *err)
{
int ret = TRANSACTION_GENERIC_ERROR;

if (!(update->flags & REF_HAVE_OLD) ||
oideq(oid, &update->old_oid))
return 0;

if (is_null_oid(&update->old_oid))
if (is_null_oid(&update->old_oid)) {
strbuf_addf(err, "cannot lock ref '%s': "
"reference already exists",
ref_update_original_update_refname(update));
ret = TRANSACTION_CREATE_EXISTS;
}
else if (is_null_oid(oid))
strbuf_addf(err, "cannot lock ref '%s': "
"reference is missing but expected %s",
@ -2522,7 +2525,7 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
oid_to_hex(oid),
oid_to_hex(&update->old_oid));

return -1;
return ret;
}

/*
@ -2602,9 +2605,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
ret = TRANSACTION_GENERIC_ERROR;
goto out;
}
} else if (check_old_oid(update, &lock->old_oid, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
} else {
ret = check_old_oid(update, &lock->old_oid, err);
if (ret) {
goto out;
}
}
} else {
/*
@ -2635,9 +2640,11 @@ static int lock_ref_for_update(struct files_ref_store *refs,
update->old_target);
ret = TRANSACTION_GENERIC_ERROR;
goto out;
} else if (check_old_oid(update, &lock->old_oid, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto out;
} else {
ret = check_old_oid(update, &lock->old_oid, err);
if (ret) {
goto out;
}
}

/*

View File

@ -673,6 +673,11 @@ struct ref_storage_be {

ref_iterator_begin_fn *iterator_begin;
read_raw_ref_fn *read_raw_ref;

/*
* Please refer to `refs_read_symbolic_ref()` for the expected
* behaviour.
*/
read_symbolic_ref_fn *read_symbolic_ref;

reflog_iterator_begin_fn *reflog_iterator_begin;

View File

@ -830,10 +830,12 @@ static int reftable_be_read_symbolic_ref(struct ref_store *ref_store,
return ret;

ret = reftable_stack_read_ref(stack, refname, &ref);
if (ret == 0 && ref.value_type == REFTABLE_REF_SYMREF)
if (ret)
ret = -1;
else if (ref.value_type == REFTABLE_REF_SYMREF)
strbuf_addstr(referent, ref.value.symref);
else
ret = -1;
ret = NOT_A_SYMREF;

reftable_ref_record_release(&ref);
return ret;
@ -1206,10 +1208,13 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
goto done;
}
} else if ((u->flags & REF_HAVE_OLD) && !oideq(&current_oid, &u->old_oid)) {
if (is_null_oid(&u->old_oid))
ret = TRANSACTION_NAME_CONFLICT;
if (is_null_oid(&u->old_oid)) {
strbuf_addf(err, _("cannot lock ref '%s': "
"reference already exists"),
ref_update_original_update_refname(u));
ret = TRANSACTION_CREATE_EXISTS;
}
else if (is_null_oid(&current_oid))
strbuf_addf(err, _("cannot lock ref '%s': "
"reference is missing but expected %s"),
@ -1221,7 +1226,6 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
ref_update_original_update_refname(u),
oid_to_hex(&current_oid),
oid_to_hex(&u->old_oid));
ret = -1;
goto done;
}


View File

@ -59,7 +59,8 @@ ${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \
${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \
${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1
${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit}, \
${c_reset}${c_remoteBranch}other/HEAD${c_reset}${c_commit})${c_reset} A1
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\
${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t
${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\

View File

@ -2,6 +2,9 @@

test_description='git remote porcelain-ish'

GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME

TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh

@ -71,7 +74,7 @@ test_expect_success 'add another remote' '
cd test &&
git remote add -f second ../two &&
tokens_match "origin second" "$(git remote)" &&
check_tracking_branch second main side another &&
check_tracking_branch second main side another HEAD &&
git for-each-ref "--format=%(refname)" refs/remotes |
sed -e "/^refs\/remotes\/origin\//d" \
-e "/^refs\/remotes\/second\//d" >actual &&
@ -429,12 +432,75 @@ test_expect_success 'set-head --auto' '
)
'

test_expect_success REFFILES 'set-head --auto failure' '
test_when_finished "rm -f test/.git/refs/remotes/origin/HEAD.lock" &&
(
cd test &&
touch .git/refs/remotes/origin/HEAD.lock &&
test_must_fail git remote set-head --auto origin 2>err &&
tail -n1 err >output &&
echo "error: Could not set up refs/remotes/origin/HEAD" >expect &&
test_cmp expect output
)
'

test_expect_success 'set-head --auto detects creation' '
(
cd test &&
git update-ref --no-deref -d refs/remotes/origin/HEAD &&
git remote set-head --auto origin >output &&
echo "${SQ}origin/HEAD${SQ} is now created and points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'

test_expect_success 'set-head --auto to update a non symbolic ref' '
(
cd test &&
git update-ref --no-deref -d refs/remotes/origin/HEAD &&
git update-ref refs/remotes/origin/HEAD HEAD &&
HEAD=$(git log --pretty="%H") &&
git remote set-head --auto origin >output &&
echo "${SQ}origin/HEAD${SQ} was detached at ${SQ}${HEAD}${SQ} and now points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'

test_expect_success 'set-head --auto detects no change' '
(
cd test &&
git remote set-head --auto origin >output &&
echo "${SQ}origin/HEAD${SQ} is unchanged and points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'

test_expect_success 'set-head --auto detects change' '
(
cd test &&
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/ahead &&
git remote set-head --auto origin >output &&
echo "${SQ}origin/HEAD${SQ} has changed from ${SQ}ahead${SQ} and now points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'

test_expect_success 'set-head --auto detects strange ref' '
(
cd test &&
git symbolic-ref refs/remotes/origin/HEAD refs/heads/main &&
git remote set-head --auto origin >output &&
echo "${SQ}origin/HEAD${SQ} used to point to ${SQ}refs/heads/main${SQ} (which is not a remote branch), but now points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'

test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
(
cd test &&
git fetch two "refs/heads/*:refs/remotes/two/*" &&
git remote set-head --auto two >output 2>&1 &&
echo "two/HEAD set to main" >expect &&
echo "${SQ}two/HEAD${SQ} is unchanged and points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'
@ -453,6 +519,16 @@ test_expect_success 'set-head explicit' '
)
'

test_expect_success 'set-head --auto reports change' '
(
cd test &&
git remote set-head origin side2 &&
git remote set-head --auto origin >output 2>&1 &&
echo "${SQ}origin/HEAD${SQ} has changed from ${SQ}side2${SQ} and now points to ${SQ}main${SQ}" >expect &&
test_cmp expect output
)
'

cat >test/expect <<EOF
Pruning origin
URL: $(pwd)/one
@ -493,6 +569,16 @@ test_expect_success 'add --mirror && prune' '
)
'

test_expect_success 'add --mirror setting HEAD' '
mkdir headmirror &&
(
cd headmirror &&
git init --bare -b notmain &&
git remote add --mirror -f origin ../one &&
test "$(git symbolic-ref HEAD)" = "refs/heads/main"
)
'

test_expect_success 'add --mirror=fetch' '
mkdir mirror-fetch &&
git init -b main mirror-fetch/parent &&
@ -712,8 +798,10 @@ test_expect_success 'reject --no-no-tags' '
'

cat >one/expect <<\EOF
apis/HEAD -> apis/main
apis/main
apis/side
drosophila/HEAD -> drosophila/main
drosophila/another
drosophila/main
drosophila/side
@ -731,11 +819,14 @@ test_expect_success 'update' '
'

cat >one/expect <<\EOF
drosophila/HEAD -> drosophila/main
drosophila/another
drosophila/main
drosophila/side
manduca/HEAD -> manduca/main
manduca/main
manduca/side
megaloprepus/HEAD -> megaloprepus/main
megaloprepus/main
megaloprepus/side
EOF
@ -743,7 +834,7 @@ EOF
test_expect_success 'update with arguments' '
(
cd one &&
for b in $(git branch -r)
for b in $(git branch -r | grep -v HEAD)
do
git branch -r -d $b || exit 1
done &&
@ -775,10 +866,13 @@ test_expect_success 'update --prune' '
'

cat >one/expect <<-\EOF
apis/HEAD -> apis/main
apis/main
apis/side
manduca/HEAD -> manduca/main
manduca/main
manduca/side
megaloprepus/HEAD -> megaloprepus/main
megaloprepus/main
megaloprepus/side
EOF
@ -786,7 +880,7 @@ EOF
test_expect_success 'update default' '
(
cd one &&
for b in $(git branch -r)
for b in $(git branch -r | grep -v HEAD)
do
git branch -r -d $b || exit 1
done &&
@ -798,6 +892,7 @@ test_expect_success 'update default' '
'

cat >one/expect <<\EOF
drosophila/HEAD -> drosophila/main
drosophila/another
drosophila/main
drosophila/side
@ -806,7 +901,7 @@ EOF
test_expect_success 'update default (overridden, with funny whitespace)' '
(
cd one &&
for b in $(git branch -r)
for b in $(git branch -r | grep -v HEAD)
do
git branch -r -d $b || exit 1
done &&
@ -820,7 +915,7 @@ test_expect_success 'update default (overridden, with funny whitespace)' '
test_expect_success 'update (with remotes.default defined)' '
(
cd one &&
for b in $(git branch -r)
for b in $(git branch -r | grep -v HEAD)
do
git branch -r -d $b || exit 1
done &&

View File

@ -75,6 +75,30 @@ test_expect_success "fetch test for-merge" '
cut -f -2 .git/FETCH_HEAD >actual &&
test_cmp expected actual'

test_expect_success "fetch test remote HEAD" '
cd "$D" &&
cd two &&
git fetch &&
git rev-parse --verify refs/remotes/origin/HEAD &&
git rev-parse --verify refs/remotes/origin/main &&
head=$(git rev-parse refs/remotes/origin/HEAD) &&
branch=$(git rev-parse refs/remotes/origin/main) &&
test "z$head" = "z$branch"'

test_expect_success "fetch test remote HEAD change" '
cd "$D" &&
cd two &&
git switch -c other &&
git push -u origin other &&
git rev-parse --verify refs/remotes/origin/HEAD &&
git rev-parse --verify refs/remotes/origin/main &&
git rev-parse --verify refs/remotes/origin/other &&
git remote set-head origin other &&
git fetch &&
head=$(git rev-parse refs/remotes/origin/HEAD) &&
branch=$(git rev-parse refs/remotes/origin/other) &&
test "z$head" = "z$branch"'

test_expect_success 'fetch --prune on its own works as expected' '
cd "$D" &&
git clone . prune &&

View File

@ -293,6 +293,8 @@ test_expect_success 'ls-remote with filtered symref (refname)' '
cat >expect <<-EOF &&
ref: refs/heads/main HEAD
$rev HEAD
ref: refs/remotes/origin/main refs/remotes/origin/HEAD
$rev refs/remotes/origin/HEAD
EOF
git ls-remote --symref . HEAD >actual &&
test_cmp expect actual

View File

@ -45,14 +45,17 @@ test_expect_success setup '
'

cat > test/expect << EOF
one/HEAD -> one/main
one/main
one/side
origin/HEAD -> origin/main
origin/main
origin/side
three/HEAD -> three/main
three/another
three/main
three/side
two/HEAD -> two/main
two/another
two/main
two/side
@ -97,6 +100,7 @@ cat > expect << EOF
origin/HEAD -> origin/main
origin/main
origin/side
three/HEAD -> three/main
three/another
three/main
three/side
@ -112,8 +116,10 @@ test_expect_success 'git fetch --multiple (but only one remote)' '
'

cat > expect << EOF
one/HEAD -> one/main
one/main
one/side
two/HEAD -> two/main
two/another
two/main
two/side
@ -141,7 +147,7 @@ test_expect_success 'git fetch --multiple (bad remote names)' '

test_expect_success 'git fetch --all (skipFetchAll)' '
(cd test4 &&
for b in $(git branch -r)
for b in $(git branch -r | grep -v HEAD)
do
git branch -r -d $b || exit 1
done &&
@ -153,11 +159,14 @@ test_expect_success 'git fetch --all (skipFetchAll)' '
'

cat > expect << EOF
one/HEAD -> one/main
one/main
one/side
three/HEAD -> three/main
three/another
three/main
three/side
two/HEAD -> two/main
two/another
two/main
two/side
@ -165,7 +174,7 @@ EOF

test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
(cd test4 &&
for b in $(git branch -r)
for b in $(git branch -r | grep -v HEAD)
do
git branch -r -d $b || exit 1
done &&
@ -221,14 +230,17 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '

create_fetch_all_expect () {
cat >expect <<-\EOF
one/HEAD -> one/main
one/main
one/side
origin/HEAD -> origin/main
origin/main
origin/side
three/HEAD -> three/main
three/another
three/main
three/side
two/HEAD -> two/main
two/another
two/main
two/side
@ -265,6 +277,7 @@ test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '

create_fetch_one_expect () {
cat >expect <<-\EOF
one/HEAD -> one/main
one/main
one/side
origin/HEAD -> origin/main

View File

@ -1395,7 +1395,8 @@ test_expect_success 'fetch follows tags by default' '
git tag -m "annotated" tag &&
git for-each-ref >tmp1 &&
sed -n "p; s|refs/heads/main$|refs/remotes/origin/main|p" tmp1 |
sort -k 3 >../expect
sed -n "p; s|refs/heads/main$|refs/remotes/origin/HEAD|p" |
sort -k 4 >../expect
) &&
test_when_finished "rm -rf dst" &&
git init dst &&

View File

@ -52,7 +52,8 @@ test_expect_success LONG_REF 'fetch handles extremely long refname' '
long
main
EOF
git for-each-ref --format="%(subject)" refs/remotes/long >actual &&
git for-each-ref --format="%(subject)" refs/remotes/long \
--exclude=refs/remotes/long/HEAD >actual &&
test_cmp expect actual
'


View File

@ -328,7 +328,8 @@ test_expect_success 'incremental-repack task' '

# Delete refs that have not been repacked in these packs.
git for-each-ref --format="delete %(refname)" \
refs/prefetch refs/tags refs/remotes >refs &&
refs/prefetch refs/tags refs/remotes \
--exclude=refs/remotes/*/HEAD >refs &&
git update-ref --stdin <refs &&

# Replace the object directory with this pack layout.

View File

@ -150,7 +150,8 @@ test_expect_success 'scalar clone' '
"$(pwd)" &&

git for-each-ref --format="%(refname)" refs/remotes/origin/ >actual &&
echo "refs/remotes/origin/parallel" >expect &&
echo "refs/remotes/origin/HEAD" >>expect &&
echo "refs/remotes/origin/parallel" >>expect &&
test_cmp expect actual &&

test_path_is_missing 1/2 &&
@ -219,7 +220,7 @@ test_expect_success 'scalar reconfigure --all with includeIf.onbranch' '
done
'

test_expect_success 'scalar reconfigure --all with detached HEADs' '
test_expect_success 'scalar reconfigure --all with detached HEADs' '
repos="two three four" &&
for num in $repos
do

View File

@ -31,7 +31,7 @@ test_expect_success 'set up repository to clone' '
)
'

cleanup_clone () {
cleanup_clone() {
rm -rf "$1"
}

@ -127,7 +127,7 @@ test_expect_success '--single-branch clones HEAD only' '
(
cd $enlistment/src &&
git for-each-ref refs/remotes/origin >out &&
test_line_count = 1 out &&
test_line_count = 2 out &&
grep "refs/remotes/origin/base" out
) &&

@ -141,7 +141,7 @@ test_expect_success '--no-single-branch clones all branches' '
(
cd $enlistment/src &&
git for-each-ref refs/remotes/origin >out &&
test_line_count = 2 out &&
test_line_count = 3 out &&
grep "refs/remotes/origin/base" out &&
grep "refs/remotes/origin/parallel" out
) &&

View File

@ -658,6 +658,7 @@ test_expect_success '__git_refs - simple' '
HEAD
main
matching-branch
other/HEAD
other/branch-in-other
other/main-in-other
matching-tag
@ -673,6 +674,7 @@ test_expect_success '__git_refs - full refs' '
cat >expected <<-EOF &&
refs/heads/main
refs/heads/matching-branch
refs/remotes/other/HEAD
refs/remotes/other/branch-in-other
refs/remotes/other/main-in-other
refs/tags/matching-tag
@ -729,6 +731,7 @@ test_expect_success '__git_refs - remote on local file system - full refs' '
test_expect_success '__git_refs - configured remote' '
cat >expected <<-EOF &&
HEAD
HEAD
branch-in-other
main-in-other
EOF
@ -756,6 +759,7 @@ test_expect_success '__git_refs - configured remote - full refs' '
test_expect_success '__git_refs - configured remote - repo given on the command line' '
cat >expected <<-EOF &&
HEAD
HEAD
branch-in-other
main-in-other
EOF
@ -787,6 +791,7 @@ test_expect_success '__git_refs - configured remote - full refs - repo given on
test_expect_success '__git_refs - configured remote - remote name matches a directory' '
cat >expected <<-EOF &&
HEAD
HEAD
branch-in-other
main-in-other
EOF
@ -875,12 +880,14 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer
HEAD
main
matching-branch
other/HEAD
other/ambiguous
other/branch-in-other
other/main-in-other
remote/ambiguous
remote/branch-in-remote
matching-tag
HEAD
branch-in-other
branch-in-remote
main-in-other
@ -904,6 +911,7 @@ test_expect_success '__git_refs - after --opt=' '
HEAD
main
matching-branch
other/HEAD
other/branch-in-other
other/main-in-other
matching-tag
@ -919,6 +927,7 @@ test_expect_success '__git_refs - after --opt= - full refs' '
cat >expected <<-EOF &&
refs/heads/main
refs/heads/matching-branch
refs/remotes/other/HEAD
refs/remotes/other/branch-in-other
refs/remotes/other/main-in-other
refs/tags/matching-tag
@ -935,6 +944,7 @@ test_expect_success '__git refs - excluding refs' '
^HEAD
^main
^matching-branch
^other/HEAD
^other/branch-in-other
^other/main-in-other
^matching-tag
@ -950,6 +960,7 @@ test_expect_success '__git refs - excluding full refs' '
cat >expected <<-EOF &&
^refs/heads/main
^refs/heads/matching-branch
^refs/remotes/other/HEAD
^refs/remotes/other/branch-in-other
^refs/remotes/other/main-in-other
^refs/tags/matching-tag
@ -975,6 +986,7 @@ test_expect_success '__git_refs - do not filter refs unless told so' '
main
matching-branch
matching/branch
other/HEAD
other/branch-in-other
other/main-in-other
other/matching/branch-in-other
@ -1095,6 +1107,7 @@ test_expect_success '__git_complete_refs - simple' '
HEAD Z
main Z
matching-branch Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
matching-tag Z
@ -1123,6 +1136,7 @@ test_expect_success '__git_complete_refs - matching' '
test_expect_success '__git_complete_refs - remote' '
sed -e "s/Z$//" >expected <<-EOF &&
HEAD Z
HEAD Z
branch-in-other Z
main-in-other Z
EOF
@ -1139,9 +1153,11 @@ test_expect_success '__git_complete_refs - track' '
HEAD Z
main Z
matching-branch Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
matching-tag Z
HEAD Z
branch-in-other Z
main-in-other Z
EOF
@ -1184,6 +1200,7 @@ test_expect_success '__git_complete_refs - suffix' '
HEAD.
main.
matching-branch.
other/HEAD.
other/branch-in-other.
other/main-in-other.
matching-tag.
@ -1199,6 +1216,7 @@ test_expect_success '__git_complete_refs - suffix' '
test_expect_success '__git_complete_fetch_refspecs - simple' '
sed -e "s/Z$//" >expected <<-EOF &&
HEAD:HEAD Z
HEAD:HEAD Z
branch-in-other:branch-in-other Z
main-in-other:main-in-other Z
EOF
@ -1225,6 +1243,7 @@ test_expect_success '__git_complete_fetch_refspecs - matching' '
test_expect_success '__git_complete_fetch_refspecs - prefix' '
sed -e "s/Z$//" >expected <<-EOF &&
+HEAD:HEAD Z
+HEAD:HEAD Z
+branch-in-other:branch-in-other Z
+main-in-other:main-in-other Z
EOF
@ -1289,6 +1308,7 @@ test_expect_success '__git_complete_worktree_paths with -C' '

test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
test_completion "git switch " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -1435,11 +1455,13 @@ test_expect_success 'git-bisect - existing view subcommand is recognized and ena
test_expect_success 'git checkout - completes refs and unique remote branches for DWIM' '
test_completion "git checkout " <<-\EOF
HEAD Z
HEAD Z
branch-in-other Z
main Z
main-in-other Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1461,6 +1483,7 @@ test_expect_success 'git switch - with GIT_COMPLETION_CHECKOUT_NO_GUESS=1, compl

test_expect_success 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete local branches and unique remote names for DWIM logic' '
GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git switch --guess " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -1470,6 +1493,7 @@ test_expect_success 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_G

test_expect_success 'git switch - a later --guess overrides previous --no-guess, complete local and remote unique branches for DWIM' '
test_completion "git switch --no-guess --guess " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -1490,6 +1514,7 @@ test_expect_success 'git checkout - with GIT_COMPLETION_NO_GUESS=1 only complete
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1498,11 +1523,13 @@ test_expect_success 'git checkout - with GIT_COMPLETION_NO_GUESS=1 only complete
test_expect_success 'git checkout - --guess overrides GIT_COMPLETION_NO_GUESS=1, complete refs and unique remote branches for DWIM' '
GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git checkout --guess " <<-\EOF
HEAD Z
HEAD Z
branch-in-other Z
main Z
main-in-other Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1514,6 +1541,7 @@ test_expect_success 'git checkout - with --no-guess, only completes refs' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1522,11 +1550,13 @@ test_expect_success 'git checkout - with --no-guess, only completes refs' '
test_expect_success 'git checkout - a later --guess overrides previous --no-guess, complete refs and unique remote branches for DWIM' '
test_completion "git checkout --no-guess --guess " <<-\EOF
HEAD Z
HEAD Z
branch-in-other Z
main Z
main-in-other Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1538,6 +1568,7 @@ test_expect_success 'git checkout - a later --no-guess overrides previous --gues
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1550,6 +1581,7 @@ test_expect_success 'git checkout - with checkout.guess = false, only completes
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1559,11 +1591,13 @@ test_expect_success 'git checkout - with checkout.guess = true, completes refs a
test_config checkout.guess true &&
test_completion "git checkout " <<-\EOF
HEAD Z
HEAD Z
branch-in-other Z
main Z
main-in-other Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1573,11 +1607,13 @@ test_expect_success 'git checkout - a later --guess overrides previous checkout.
test_config checkout.guess false &&
test_completion "git checkout --guess " <<-\EOF
HEAD Z
HEAD Z
branch-in-other Z
main Z
main-in-other Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1590,6 +1626,7 @@ test_expect_success 'git checkout - a later --no-guess overrides previous checko
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1601,6 +1638,7 @@ test_expect_success 'git switch - with --detach, complete all references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1612,6 +1650,7 @@ test_expect_success 'git checkout - with --detach, complete only references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1783,6 +1822,7 @@ test_expect_success 'git switch - with -d, complete all references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1794,6 +1834,7 @@ test_expect_success 'git checkout - with -d, complete only references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1801,10 +1842,12 @@ test_expect_success 'git checkout - with -d, complete only references' '

test_expect_success 'git switch - with --track, complete only remote branches' '
test_completion "git switch --track " <<-\EOF &&
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
test_completion "git switch -t " <<-\EOF
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1812,10 +1855,12 @@ test_expect_success 'git switch - with --track, complete only remote branches' '

test_expect_success 'git checkout - with --track, complete only remote branches' '
test_completion "git checkout --track " <<-\EOF &&
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
test_completion "git checkout -t " <<-\EOF
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1834,6 +1879,7 @@ test_expect_success 'git checkout - with --no-track, complete only local referen
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1845,6 +1891,7 @@ test_expect_success 'git switch - with -c, complete all references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1856,6 +1903,7 @@ test_expect_success 'git switch - with -C, complete all references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1867,6 +1915,7 @@ test_expect_success 'git switch - with -c and --track, complete all references'
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1878,6 +1927,7 @@ test_expect_success 'git switch - with -C and --track, complete all references'
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1889,6 +1939,7 @@ test_expect_success 'git switch - with -c and --no-track, complete all reference
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1900,6 +1951,7 @@ test_expect_success 'git switch - with -C and --no-track, complete all reference
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1911,6 +1963,7 @@ test_expect_success 'git checkout - with -b, complete all references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1922,6 +1975,7 @@ test_expect_success 'git checkout - with -B, complete all references' '
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1933,6 +1987,7 @@ test_expect_success 'git checkout - with -b and --track, complete all references
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1944,6 +1999,7 @@ test_expect_success 'git checkout - with -B and --track, complete all references
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1955,6 +2011,7 @@ test_expect_success 'git checkout - with -b and --no-track, complete all referen
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1966,6 +2023,7 @@ test_expect_success 'git checkout - with -B and --no-track, complete all referen
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF
@ -1973,6 +2031,7 @@ test_expect_success 'git checkout - with -B and --no-track, complete all referen

test_expect_success 'git switch - for -c, complete local branches and unique remote branches' '
test_completion "git switch -c " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -1982,6 +2041,7 @@ test_expect_success 'git switch - for -c, complete local branches and unique rem

test_expect_success 'git switch - for -C, complete local branches and unique remote branches' '
test_completion "git switch -C " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -2019,6 +2079,7 @@ test_expect_success 'git switch - for -C with --no-track, complete local branche

test_expect_success 'git checkout - for -b, complete local branches and unique remote branches' '
test_completion "git checkout -b " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -2028,6 +2089,7 @@ test_expect_success 'git checkout - for -b, complete local branches and unique r

test_expect_success 'git checkout - for -B, complete local branches and unique remote branches' '
test_completion "git checkout -B " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -2065,6 +2127,7 @@ test_expect_success 'git checkout - for -B with --no-track, complete local branc

test_expect_success 'git switch - with --orphan completes local branch names and unique remote branch names' '
test_completion "git switch --orphan " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -2080,6 +2143,7 @@ test_expect_success 'git switch - --orphan with branch already provided complete

test_expect_success 'git checkout - with --orphan completes local branch names and unique remote branch names' '
test_completion "git checkout --orphan " <<-\EOF
HEAD Z
branch-in-other Z
main Z
main-in-other Z
@ -2093,6 +2157,7 @@ test_expect_success 'git checkout - --orphan with branch already provided comple
main Z
matching-branch Z
matching-tag Z
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
EOF