Skip to content

Commit

Permalink
Merge branch 'jk/fetch-always-update-tracking'
Browse files Browse the repository at this point in the history
"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
  • Loading branch information
Junio C Hamano committed Jun 2, 2013
2 parents 67b57a9 + 823c6d5 commit db40094
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 30 deletions.
11 changes: 8 additions & 3 deletions Documentation/pull-fetch-param.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ Some short-cut notations are also supported.
+
* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
it requests fetching everything up to the given tag.
* A parameter <ref> without a colon is equivalent to
<ref>: when pulling/fetching, so it merges <ref> into the current
branch without storing the remote branch anywhere locally
ifndef::git-pull[]
* A parameter <ref> without a colon fetches that ref into FETCH_HEAD,
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).
73 changes: 51 additions & 22 deletions builtin/fetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ static void add_merge_config(struct ref **head,

for (rm = *head; rm; rm = rm->next) {
if (branch_merge_matches(branch, i, rm->name)) {
rm->merge = 1;
rm->fetch_head_status = FETCH_HEAD_MERGE;
break;
}
}
Expand All @@ -140,7 +140,7 @@ static void add_merge_config(struct ref **head,
refspec.src = branch->merge[i]->src;
get_fetch_map(remote_refs, &refspec, tail, 1);
for (rm = *old_tail; rm; rm = rm->next)
rm->merge = 1;
rm->fetch_head_status = FETCH_HEAD_MERGE;
}
}

Expand All @@ -160,16 +160,32 @@ static struct ref *get_ref_map(struct transport *transport,
const struct ref *remote_refs = transport_get_remote_refs(transport);

if (ref_count || tags == TAGS_SET) {
struct ref **old_tail;

for (i = 0; i < ref_count; i++) {
get_fetch_map(remote_refs, &refs[i], &tail, 0);
if (refs[i].dst && refs[i].dst[0])
*autotags = 1;
}
/* Merge everything on the command line, but not --tags */
for (rm = ref_map; rm; rm = rm->next)
rm->merge = 1;
rm->fetch_head_status = FETCH_HEAD_MERGE;
if (tags == TAGS_SET)
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 {
/* Use the defaults */
struct remote *remote = transport->remote;
Expand All @@ -186,7 +202,7 @@ static struct ref *get_ref_map(struct transport *transport,
*autotags = 1;
if (!i && !has_merge && ref_map &&
!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
Expand All @@ -202,7 +218,7 @@ static struct ref *get_ref_map(struct transport *transport,
ref_map = get_remote_ref(remote_refs, "HEAD");
if (!ref_map)
die(_("Couldn't find remote ref HEAD"));
ref_map->merge = 1;
ref_map->fetch_head_status = FETCH_HEAD_MERGE;
tail = &ref_map->next;
}
}
Expand Down Expand Up @@ -389,7 +405,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
const char *what, *kind;
struct ref *rm;
char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
int want_merge;
int want_status;

fp = fopen(filename, "a");
if (!fp)
Expand All @@ -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
* second pass writes the rest, in order to allow using
* FETCH_HEAD as a refname to refer to the ref to be merged.
* We do a pass for each fetch_head_status type in their enum order, so
* merged entries are written before not-for-merge. That lets readers
* 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) {
struct ref *ref = NULL;
const char *merge_status_marker = "";

commit = lookup_commit_reference_gently(rm->old_sha1, 1);
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;

if (rm->peer_ref) {
Expand Down Expand Up @@ -465,16 +484,26 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
strbuf_addf(&note, "%s ", kind);
strbuf_addf(&note, "'%s' of ", what);
}
fprintf(fp, "%s\t%s\t%s",
sha1_to_hex(rm->old_sha1),
rm->merge ? "" : "not-for-merge",
note.buf);
for (i = 0; i < url_len; ++i)
if ('\n' == url[i])
fputs("\\n", fp);
else
fputc(url[i], fp);
fputc('\n', fp);
switch (rm->fetch_head_status) {
case FETCH_HEAD_NOT_FOR_MERGE:
merge_status_marker = "not-for-merge";
/* fall-through */
case FETCH_HEAD_MERGE:
fprintf(fp, "%s\t%s\t%s",
sha1_to_hex(rm->old_sha1),
merge_status_marker,
note.buf);
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(&note);
if (ref) {
Expand Down
14 changes: 13 additions & 1 deletion cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -1025,9 +1025,21 @@ struct ref {
unsigned int
force:1,
forced_update:1,
merge:1,
deletion: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 {
REF_STATUS_NONE = 0,
REF_STATUS_OK,
Expand Down
34 changes: 30 additions & 4 deletions t/t5510-fetch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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" &&
git branch -f side &&
(
cd three &&
git update-ref refs/remotes/origin/master base-origin-master &&
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 "$o" != "$n" &&
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" &&
git branch -f side &&
(
cd three &&
git update-ref refs/remotes/origin/master base-origin-master &&
o=$(git rev-parse --verify refs/remotes/origin/master) &&
git pull 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
)
'
Expand All @@ -404,6 +413,7 @@ test_expect_success 'configured fetch updates tracking' '
git branch -f side &&
(
cd three &&
git update-ref refs/remotes/origin/master base-origin-master &&
o=$(git rev-parse --verify refs/remotes/origin/master) &&
git fetch origin &&
n=$(git rev-parse --verify refs/remotes/origin/master) &&
Expand All @@ -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' '
cd "$D" &&
Expand Down

0 comments on commit db40094

Please sign in to comment.