Merge branch 'jk/fetch-always-update-tracking'
"git fetch origin master" unlike "git fetch origin" or "git fetch" did not update "refs/remotes/origin/master"; this was an early design decision to keep the update of remote tracking branches predictable, but in practice it turns out that people find it more convenient to opportunisticly update them whenever we have a chance, and we have been updating them when we run "git push" which already breaks the original "predictability" anyway. Now such a fetch does update refs/remotes/origin/master. * jk/fetch-always-update-tracking: fetch: don't try to update unfetched tracking refs fetch: opportunistically update tracking refs refactor "ref->merge" flag fetch/pull doc: untangle meaning of bare <ref> t5510: start tracking-ref tests from a known state
This commit is contained in:
commit
db400949b3
@ -68,6 +68,11 @@ Some short-cut notations are also supported.
|
|||||||
+
|
+
|
||||||
* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
|
* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
|
||||||
it requests fetching everything up to the given tag.
|
it requests fetching everything up to the given tag.
|
||||||
* A parameter <ref> without a colon is equivalent to
|
ifndef::git-pull[]
|
||||||
<ref>: when pulling/fetching, so it merges <ref> into the current
|
* A parameter <ref> without a colon fetches that ref into FETCH_HEAD,
|
||||||
branch without storing the remote branch anywhere locally
|
endif::git-pull[]
|
||||||
|
ifdef::git-pull[]
|
||||||
|
* A parameter <ref> without a colon merges <ref> into the current
|
||||||
|
branch,
|
||||||
|
endif::git-pull[]
|
||||||
|
and updates the remote-tracking branches (if any).
|
||||||
|
@ -119,7 +119,7 @@ static void add_merge_config(struct ref **head,
|
|||||||
|
|
||||||
for (rm = *head; rm; rm = rm->next) {
|
for (rm = *head; rm; rm = rm->next) {
|
||||||
if (branch_merge_matches(branch, i, rm->name)) {
|
if (branch_merge_matches(branch, i, rm->name)) {
|
||||||
rm->merge = 1;
|
rm->fetch_head_status = FETCH_HEAD_MERGE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ static void add_merge_config(struct ref **head,
|
|||||||
refspec.src = branch->merge[i]->src;
|
refspec.src = branch->merge[i]->src;
|
||||||
get_fetch_map(remote_refs, &refspec, tail, 1);
|
get_fetch_map(remote_refs, &refspec, tail, 1);
|
||||||
for (rm = *old_tail; rm; rm = rm->next)
|
for (rm = *old_tail; rm; rm = rm->next)
|
||||||
rm->merge = 1;
|
rm->fetch_head_status = FETCH_HEAD_MERGE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,6 +160,8 @@ static struct ref *get_ref_map(struct transport *transport,
|
|||||||
const struct ref *remote_refs = transport_get_remote_refs(transport);
|
const struct ref *remote_refs = transport_get_remote_refs(transport);
|
||||||
|
|
||||||
if (ref_count || tags == TAGS_SET) {
|
if (ref_count || tags == TAGS_SET) {
|
||||||
|
struct ref **old_tail;
|
||||||
|
|
||||||
for (i = 0; i < ref_count; i++) {
|
for (i = 0; i < ref_count; i++) {
|
||||||
get_fetch_map(remote_refs, &refs[i], &tail, 0);
|
get_fetch_map(remote_refs, &refs[i], &tail, 0);
|
||||||
if (refs[i].dst && refs[i].dst[0])
|
if (refs[i].dst && refs[i].dst[0])
|
||||||
@ -167,9 +169,23 @@ static struct ref *get_ref_map(struct transport *transport,
|
|||||||
}
|
}
|
||||||
/* Merge everything on the command line, but not --tags */
|
/* Merge everything on the command line, but not --tags */
|
||||||
for (rm = ref_map; rm; rm = rm->next)
|
for (rm = ref_map; rm; rm = rm->next)
|
||||||
rm->merge = 1;
|
rm->fetch_head_status = FETCH_HEAD_MERGE;
|
||||||
if (tags == TAGS_SET)
|
if (tags == TAGS_SET)
|
||||||
get_fetch_map(remote_refs, tag_refspec, &tail, 0);
|
get_fetch_map(remote_refs, tag_refspec, &tail, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For any refs that we happen to be fetching via command-line
|
||||||
|
* arguments, take the opportunity to update their configured
|
||||||
|
* counterparts. However, we do not want to mention these
|
||||||
|
* entries in FETCH_HEAD at all, as they would simply be
|
||||||
|
* duplicates of existing entries.
|
||||||
|
*/
|
||||||
|
old_tail = tail;
|
||||||
|
for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
|
||||||
|
get_fetch_map(ref_map, &transport->remote->fetch[i],
|
||||||
|
&tail, 1);
|
||||||
|
for (rm = *old_tail; rm; rm = rm->next)
|
||||||
|
rm->fetch_head_status = FETCH_HEAD_IGNORE;
|
||||||
} else {
|
} else {
|
||||||
/* Use the defaults */
|
/* Use the defaults */
|
||||||
struct remote *remote = transport->remote;
|
struct remote *remote = transport->remote;
|
||||||
@ -186,7 +202,7 @@ static struct ref *get_ref_map(struct transport *transport,
|
|||||||
*autotags = 1;
|
*autotags = 1;
|
||||||
if (!i && !has_merge && ref_map &&
|
if (!i && !has_merge && ref_map &&
|
||||||
!remote->fetch[0].pattern)
|
!remote->fetch[0].pattern)
|
||||||
ref_map->merge = 1;
|
ref_map->fetch_head_status = FETCH_HEAD_MERGE;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* if the remote we're fetching from is the same
|
* if the remote we're fetching from is the same
|
||||||
@ -202,7 +218,7 @@ static struct ref *get_ref_map(struct transport *transport,
|
|||||||
ref_map = get_remote_ref(remote_refs, "HEAD");
|
ref_map = get_remote_ref(remote_refs, "HEAD");
|
||||||
if (!ref_map)
|
if (!ref_map)
|
||||||
die(_("Couldn't find remote ref HEAD"));
|
die(_("Couldn't find remote ref HEAD"));
|
||||||
ref_map->merge = 1;
|
ref_map->fetch_head_status = FETCH_HEAD_MERGE;
|
||||||
tail = &ref_map->next;
|
tail = &ref_map->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -389,7 +405,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
|
|||||||
const char *what, *kind;
|
const char *what, *kind;
|
||||||
struct ref *rm;
|
struct ref *rm;
|
||||||
char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
|
char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
|
||||||
int want_merge;
|
int want_status;
|
||||||
|
|
||||||
fp = fopen(filename, "a");
|
fp = fopen(filename, "a");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
@ -407,19 +423,22 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The first pass writes objects to be merged and then the
|
* We do a pass for each fetch_head_status type in their enum order, so
|
||||||
* second pass writes the rest, in order to allow using
|
* merged entries are written before not-for-merge. That lets readers
|
||||||
* FETCH_HEAD as a refname to refer to the ref to be merged.
|
* use FETCH_HEAD as a refname to refer to the ref to be merged.
|
||||||
*/
|
*/
|
||||||
for (want_merge = 1; 0 <= want_merge; want_merge--) {
|
for (want_status = FETCH_HEAD_MERGE;
|
||||||
|
want_status <= FETCH_HEAD_IGNORE;
|
||||||
|
want_status++) {
|
||||||
for (rm = ref_map; rm; rm = rm->next) {
|
for (rm = ref_map; rm; rm = rm->next) {
|
||||||
struct ref *ref = NULL;
|
struct ref *ref = NULL;
|
||||||
|
const char *merge_status_marker = "";
|
||||||
|
|
||||||
commit = lookup_commit_reference_gently(rm->old_sha1, 1);
|
commit = lookup_commit_reference_gently(rm->old_sha1, 1);
|
||||||
if (!commit)
|
if (!commit)
|
||||||
rm->merge = 0;
|
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
|
||||||
|
|
||||||
if (rm->merge != want_merge)
|
if (rm->fetch_head_status != want_status)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (rm->peer_ref) {
|
if (rm->peer_ref) {
|
||||||
@ -465,16 +484,26 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
|
|||||||
strbuf_addf(¬e, "%s ", kind);
|
strbuf_addf(¬e, "%s ", kind);
|
||||||
strbuf_addf(¬e, "'%s' of ", what);
|
strbuf_addf(¬e, "'%s' of ", what);
|
||||||
}
|
}
|
||||||
fprintf(fp, "%s\t%s\t%s",
|
switch (rm->fetch_head_status) {
|
||||||
sha1_to_hex(rm->old_sha1),
|
case FETCH_HEAD_NOT_FOR_MERGE:
|
||||||
rm->merge ? "" : "not-for-merge",
|
merge_status_marker = "not-for-merge";
|
||||||
note.buf);
|
/* fall-through */
|
||||||
for (i = 0; i < url_len; ++i)
|
case FETCH_HEAD_MERGE:
|
||||||
if ('\n' == url[i])
|
fprintf(fp, "%s\t%s\t%s",
|
||||||
fputs("\\n", fp);
|
sha1_to_hex(rm->old_sha1),
|
||||||
else
|
merge_status_marker,
|
||||||
fputc(url[i], fp);
|
note.buf);
|
||||||
fputc('\n', fp);
|
for (i = 0; i < url_len; ++i)
|
||||||
|
if ('\n' == url[i])
|
||||||
|
fputs("\\n", fp);
|
||||||
|
else
|
||||||
|
fputc(url[i], fp);
|
||||||
|
fputc('\n', fp);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* do not write anything to FETCH_HEAD */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
strbuf_reset(¬e);
|
strbuf_reset(¬e);
|
||||||
if (ref) {
|
if (ref) {
|
||||||
|
14
cache.h
14
cache.h
@ -1025,9 +1025,21 @@ struct ref {
|
|||||||
unsigned int
|
unsigned int
|
||||||
force:1,
|
force:1,
|
||||||
forced_update:1,
|
forced_update:1,
|
||||||
merge:1,
|
|
||||||
deletion:1,
|
deletion:1,
|
||||||
matched:1;
|
matched:1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Order is important here, as we write to FETCH_HEAD
|
||||||
|
* in numeric order. And the default NOT_FOR_MERGE
|
||||||
|
* should be 0, so that xcalloc'd structures get it
|
||||||
|
* by default.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
FETCH_HEAD_MERGE = -1,
|
||||||
|
FETCH_HEAD_NOT_FOR_MERGE = 0,
|
||||||
|
FETCH_HEAD_IGNORE = 1
|
||||||
|
} fetch_head_status;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
REF_STATUS_NONE = 0,
|
REF_STATUS_NONE = 0,
|
||||||
REF_STATUS_OK,
|
REF_STATUS_OK,
|
||||||
|
@ -370,30 +370,39 @@ test_expect_success 'bundle should record HEAD correctly' '
|
|||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'explicit fetch should not update tracking' '
|
test_expect_success 'mark initial state of origin/master' '
|
||||||
|
(
|
||||||
|
cd three &&
|
||||||
|
git tag base-origin-master refs/remotes/origin/master
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'explicit fetch should update tracking' '
|
||||||
|
|
||||||
cd "$D" &&
|
cd "$D" &&
|
||||||
git branch -f side &&
|
git branch -f side &&
|
||||||
(
|
(
|
||||||
cd three &&
|
cd three &&
|
||||||
|
git update-ref refs/remotes/origin/master base-origin-master &&
|
||||||
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
git fetch origin master &&
|
git fetch origin master &&
|
||||||
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
test "$o" = "$n" &&
|
test "$o" != "$n" &&
|
||||||
test_must_fail git rev-parse --verify refs/remotes/origin/side
|
test_must_fail git rev-parse --verify refs/remotes/origin/side
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'explicit pull should not update tracking' '
|
test_expect_success 'explicit pull should update tracking' '
|
||||||
|
|
||||||
cd "$D" &&
|
cd "$D" &&
|
||||||
git branch -f side &&
|
git branch -f side &&
|
||||||
(
|
(
|
||||||
cd three &&
|
cd three &&
|
||||||
|
git update-ref refs/remotes/origin/master base-origin-master &&
|
||||||
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
git pull origin master &&
|
git pull origin master &&
|
||||||
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
test "$o" = "$n" &&
|
test "$o" != "$n" &&
|
||||||
test_must_fail git rev-parse --verify refs/remotes/origin/side
|
test_must_fail git rev-parse --verify refs/remotes/origin/side
|
||||||
)
|
)
|
||||||
'
|
'
|
||||||
@ -404,6 +413,7 @@ test_expect_success 'configured fetch updates tracking' '
|
|||||||
git branch -f side &&
|
git branch -f side &&
|
||||||
(
|
(
|
||||||
cd three &&
|
cd three &&
|
||||||
|
git update-ref refs/remotes/origin/master base-origin-master &&
|
||||||
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
git fetch origin &&
|
git fetch origin &&
|
||||||
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
@ -412,6 +422,22 @@ test_expect_success 'configured fetch updates tracking' '
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'non-matching refspecs do not confuse tracking update' '
|
||||||
|
cd "$D" &&
|
||||||
|
git update-ref refs/odd/location HEAD &&
|
||||||
|
(
|
||||||
|
cd three &&
|
||||||
|
git update-ref refs/remotes/origin/master base-origin-master &&
|
||||||
|
git config --add remote.origin.fetch \
|
||||||
|
refs/odd/location:refs/remotes/origin/odd &&
|
||||||
|
o=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
|
git fetch origin master &&
|
||||||
|
n=$(git rev-parse --verify refs/remotes/origin/master) &&
|
||||||
|
test "$o" != "$n" &&
|
||||||
|
test_must_fail git rev-parse --verify refs/remotes/origin/odd
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'pushing nonexistent branch by mistake should not segv' '
|
test_expect_success 'pushing nonexistent branch by mistake should not segv' '
|
||||||
|
|
||||||
cd "$D" &&
|
cd "$D" &&
|
||||||
|
Loading…
Reference in New Issue
Block a user