merge: allow fast-forward when merging a tracked tag
Long time ago at fab47d05 ("merge: force edit and no-ff mode when
merging a tag object", 2011-11-07), "git merge" was made to always
create a merge commit when merging a tag, even when the side branch
being merged is a descendant of the current branch.
This default is good for merges made by upstream maintainers to
integrate work signed by downstream contributors, but will leave
pointless no-ff merges when downstream contributors pull a newer
release tag to make their long-running topic branches catch up with
the upstream. When there is no local work left on the topic, such a
merge should simply fast-forward to the commit pointed at by the
release tag.
Update the default (again) for "git merge" that merges a tag object
to (1) --no-ff (i.e. create a merge commit even when side branch
fast forwards) if the tag being merged is not at its expected place
in refs/tags/ hierarchy and (2) --ff (i.e. allow fast-forward update
when able) otherwise.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
@ -33,6 +33,7 @@
|
||||
#include "sequencer.h"
|
||||
#include "string-list.h"
|
||||
#include "packfile.h"
|
||||
#include "tag.h"
|
||||
|
||||
#define DEFAULT_TWOHEAD (1<<0)
|
||||
#define DEFAULT_OCTOPUS (1<<1)
|
||||
@ -1125,6 +1126,43 @@ static struct commit_list *collect_parents(struct commit *head_commit,
|
||||
return remoteheads;
|
||||
}
|
||||
|
||||
static int merging_a_throwaway_tag(struct commit *commit)
|
||||
{
|
||||
char *tag_ref;
|
||||
struct object_id oid;
|
||||
int is_throwaway_tag = 0;
|
||||
|
||||
/* Are we merging a tag? */
|
||||
if (!merge_remote_util(commit) ||
|
||||
!merge_remote_util(commit)->obj ||
|
||||
merge_remote_util(commit)->obj->type != OBJ_TAG)
|
||||
return is_throwaway_tag;
|
||||
|
||||
/*
|
||||
* Now we know we are merging a tag object. Are we downstream
|
||||
* and following the tags from upstream? If so, we must have
|
||||
* the tag object pointed at by "refs/tags/$T" where $T is the
|
||||
* tagname recorded in the tag object. We want to allow such
|
||||
* a "just to catch up" merge to fast-forward.
|
||||
*
|
||||
* Otherwise, we are playing an integrator's role, making a
|
||||
* merge with a throw-away tag from a contributor with
|
||||
* something like "git pull $contributor $signed_tag".
|
||||
* We want to forbid such a merge from fast-forwarding
|
||||
* by default; otherwise we would not keep the signature
|
||||
* anywhere.
|
||||
*/
|
||||
tag_ref = xstrfmt("refs/tags/%s",
|
||||
((struct tag *)merge_remote_util(commit)->obj)->tag);
|
||||
if (!read_ref(tag_ref, &oid) &&
|
||||
!oidcmp(&oid, &merge_remote_util(commit)->obj->oid))
|
||||
is_throwaway_tag = 0;
|
||||
else
|
||||
is_throwaway_tag = 1;
|
||||
free(tag_ref);
|
||||
return is_throwaway_tag;
|
||||
}
|
||||
|
||||
int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct object_id result_tree, stash, head_oid;
|
||||
@ -1322,10 +1360,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
oid_to_hex(&commit->object.oid));
|
||||
setenv(buf.buf, merge_remote_util(commit)->name, 1);
|
||||
strbuf_reset(&buf);
|
||||
if (fast_forward != FF_ONLY &&
|
||||
merge_remote_util(commit) &&
|
||||
merge_remote_util(commit)->obj &&
|
||||
merge_remote_util(commit)->obj->type == OBJ_TAG)
|
||||
if (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit))
|
||||
fast_forward = FF_NO;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user