Skip to content

Commit cbccd36

Browse files
committed
Merge branch 'jc/merge-base-fp-only' (early part) into pu
* 'jc/merge-base-fp-only' (early part): merge: allow to use only the fp-only merge bases merge-base: limit the output to bases that are on first-parent chain merge-base: mark bases that are on first-parent chain merge-base: expose get_merge_bases_many_0() a bit more merge-base: stop moving commits around in remove_redundant() sha1_name: remove ONELINE_SEEN bit commit: simplify fastpath of merge-base
2 parents 275abed + 26cc188 commit cbccd36

File tree

8 files changed

+94
-49
lines changed

8 files changed

+94
-49
lines changed

Documentation/git-merge-base.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-merge-base - Find as good common ancestors as possible for a merge
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git merge-base' [-a|--all] <commit> <commit>...
12+
'git merge-base' [-a|--all] [--fp-only] <commit> <commit>...
1313
'git merge-base' [-a|--all] --octopus <commit>...
1414
'git merge-base' --is-ancestor <commit> <commit>
1515
'git merge-base' --independent <commit>...
@@ -72,6 +72,12 @@ OPTIONS
7272
--all::
7373
Output all merge bases for the commits, instead of just one.
7474

75+
--fp-only::
76+
Limit the output to merge bases that are on the first-parent
77+
chain from none of the commits given on the command line.
78+
79+
80+
7581
DISCUSSION
7682
----------
7783

Documentation/merge-options.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ ifndef::git-pull[]
116116
Note that not all merge strategies may support progress
117117
reporting.
118118

119+
--fp-base-only::
120+
Instead of using all merge bases when computing the
121+
three-way merge result, use only the merge bases on the
122+
first-parent chain of the commits being merged. This
123+
experimental feature is meant to be used when merging an
124+
older integration branch back to a newer integration branch
125+
in the topic-branch workflow.
126+
127+
119128
endif::git-pull[]
120129

121130
--allow-unrelated-histories::

builtin/merge-base.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
#include "revision.h"
77
#include "parse-options.h"
88

9-
static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
9+
static int show_merge_base(struct commit **rev, int rev_nr, int show_all, int fp_only)
1010
{
1111
struct commit_list *result;
12+
unsigned flags = fp_only ? MB_FPCHAIN : 0;
1213

13-
result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1);
14+
result = get_merge_bases_opt(rev[0], rev_nr - 1, rev + 1, flags);
1415

1516
if (!result)
1617
return 1;
@@ -211,10 +212,13 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
211212
struct commit **rev;
212213
int rev_nr = 0;
213214
int show_all = 0;
215+
int fp_only = 0;
214216
int cmdmode = 0;
215217

216218
struct option options[] = {
217219
OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")),
220+
OPT_BOOL(0, "fp-only", &fp_only,
221+
N_("limit to bases that are on first-parent chain")),
218222
OPT_CMDMODE(0, "octopus", &cmdmode,
219223
N_("find ancestors for a single n-way merge"), 'o'),
220224
OPT_CMDMODE(0, "independent", &cmdmode,
@@ -258,5 +262,5 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
258262
ALLOC_ARRAY(rev, argc);
259263
while (argc-- > 0)
260264
rev[rev_nr++] = get_commit_reference(*argv++);
261-
return show_merge_base(rev, rev_nr, show_all);
265+
return show_merge_base(rev, rev_nr, show_all, fp_only);
262266
}

builtin/merge.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ enum ff_type {
8787

8888
static enum ff_type fast_forward = FF_ALLOW;
8989

90+
static int fp_base_only;
91+
9092
static int option_parse_message(const struct option *opt,
9193
const char *arg, int unset)
9294
{
@@ -209,6 +211,8 @@ static struct option builtin_merge_options[] = {
209211
{ OPTION_SET_INT, 0, "ff-only", &fast_forward, NULL,
210212
N_("abort if fast-forward is not possible"),
211213
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, FF_ONLY },
214+
OPT_BOOL(0, "fp-base-only", &fp_base_only,
215+
N_("use only merge bases on first-parent chain")),
212216
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
213217
OPT_BOOL(0, "verify-signatures", &verify_signatures,
214218
N_("verify that the named commit has a valid GPG signature")),
@@ -1299,9 +1303,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
12991303

13001304
if (!remoteheads)
13011305
; /* already up-to-date */
1302-
else if (!remoteheads->next)
1303-
common = get_merge_bases(head_commit, remoteheads->item);
1304-
else {
1306+
else if (!remoteheads->next) {
1307+
unsigned flags = MB_POSTCLEAN;
1308+
if (fp_base_only)
1309+
flags |= MB_FPCHAIN;
1310+
common = get_merge_bases_opt(head_commit,
1311+
1, &remoteheads->item,
1312+
flags);
1313+
} else {
13051314
struct commit_list *list = remoteheads;
13061315
commit_list_insert(head_commit, &list);
13071316
common = get_octopus_merge_bases(list);

commit.c

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -764,8 +764,9 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
764764
#define PARENT2 (1u<<17)
765765
#define STALE (1u<<18)
766766
#define RESULT (1u<<19)
767+
#define FPCHAIN (1u<<20)
767768

768-
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
769+
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT | FPCHAIN);
769770

770771
static int queue_has_nonstale(struct prio_queue *queue)
771772
{
@@ -801,6 +802,7 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
801802
struct commit *commit = prio_queue_get(&queue);
802803
struct commit_list *parents;
803804
int flags;
805+
int nth_parent = 0;
804806

805807
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
806808
if (flags == (PARENT1 | PARENT2)) {
@@ -815,11 +817,14 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
815817
while (parents) {
816818
struct commit *p = parents->item;
817819
parents = parents->next;
820+
nth_parent++;
818821
if ((p->object.flags & flags) == flags)
819822
continue;
820823
if (parse_commit(p))
821824
return NULL;
822825
p->object.flags |= flags;
826+
if (nth_parent == 1)
827+
p->object.flags |= FPCHAIN;
823828
prio_queue_put(&queue, p);
824829
}
825830
}
@@ -887,11 +892,11 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
887892
return ret;
888893
}
889894

890-
static int remove_redundant(struct commit **array, int cnt)
895+
static void mark_redundant(struct commit **array, int cnt)
891896
{
892897
/*
893898
* Some commit in the array may be an ancestor of
894-
* another commit. Move such commit to the end of
899+
* another commit. Mark such commit as STALE in
895900
* the array, and return the number of commits that
896901
* are independent from each other.
897902
*/
@@ -929,35 +934,42 @@ static int remove_redundant(struct commit **array, int cnt)
929934
free_commit_list(common);
930935
}
931936

932-
/* Now collect the result */
933-
COPY_ARRAY(work, array, cnt);
934-
for (i = filled = 0; i < cnt; i++)
935-
if (!redundant[i])
936-
array[filled++] = work[i];
937-
for (j = filled, i = 0; i < cnt; i++)
937+
/* Mark the result */
938+
for (i = 0; i < cnt; i++)
938939
if (redundant[i])
939-
array[j++] = work[i];
940+
array[i]->object.flags |= STALE;
941+
else
942+
array[i]->object.flags &= ~STALE;
943+
940944
free(work);
941945
free(redundant);
942946
free(filled_index);
943-
return filled;
944947
}
945948

946-
static struct commit_list *get_merge_bases_many_0(struct commit *one,
947-
int n,
948-
struct commit **twos,
949-
int cleanup)
949+
struct commit_list *get_merge_bases_opt(struct commit *one,
950+
int n, struct commit **twos,
951+
unsigned flags)
950952
{
951953
struct commit_list *list;
952954
struct commit **rslt;
953955
struct commit_list *result;
954956
int cnt, i;
957+
int cleanup = !!(flags & MB_POSTCLEAN);
958+
int fpchain = !!(flags & MB_FPCHAIN);
959+
char *on_fpchain;
955960

956961
result = merge_bases_many(one, n, twos);
957-
for (i = 0; i < n; i++) {
958-
if (one == twos[i])
959-
return result;
960-
}
962+
963+
/*
964+
* The fast-path of 'one' being the merge-base; there is no
965+
* need to clean the object flags in this case.
966+
*/
967+
if (result && !result->next &&
968+
result->item == one &&
969+
!(one->object.flags & RESULT))
970+
return result;
971+
972+
/* If we didn't get any, or there is only one, we are done */
961973
if (!result || !result->next) {
962974
if (cleanup) {
963975
clear_commit_marks(one, all_flags);
@@ -969,38 +981,40 @@ static struct commit_list *get_merge_bases_many_0(struct commit *one,
969981
/* There are more than one */
970982
cnt = commit_list_count(result);
971983
rslt = xcalloc(cnt, sizeof(*rslt));
984+
on_fpchain = xcalloc(cnt, sizeof(*on_fpchain));
972985
for (list = result, i = 0; list; list = list->next)
973986
rslt[i++] = list->item;
974987
free_commit_list(result);
975988

989+
for (i = 0; i < cnt; i++)
990+
on_fpchain[i] = !!(rslt[i]->object.flags & FPCHAIN);
991+
976992
clear_commit_marks(one, all_flags);
977993
clear_commit_marks_many(n, twos, all_flags);
978994

979-
cnt = remove_redundant(rslt, cnt);
995+
mark_redundant(rslt, cnt);
980996
result = NULL;
981997
for (i = 0; i < cnt; i++)
982-
commit_list_insert_by_date(rslt[i], &result);
998+
if (!(rslt[i]->object.flags & STALE) &&
999+
(!fpchain || on_fpchain[i]))
1000+
commit_list_insert_by_date(rslt[i], &result);
1001+
else
1002+
rslt[i]->object.flags &= ~STALE;
9831003
free(rslt);
1004+
free(on_fpchain);
9841005
return result;
9851006
}
9861007

9871008
struct commit_list *get_merge_bases_many(struct commit *one,
9881009
int n,
9891010
struct commit **twos)
9901011
{
991-
return get_merge_bases_many_0(one, n, twos, 1);
992-
}
993-
994-
struct commit_list *get_merge_bases_many_dirty(struct commit *one,
995-
int n,
996-
struct commit **twos)
997-
{
998-
return get_merge_bases_many_0(one, n, twos, 0);
1012+
return get_merge_bases_opt(one, n, twos, 0);
9991013
}
10001014

10011015
struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
10021016
{
1003-
return get_merge_bases_many_0(one, 1, &two, 1);
1017+
return get_merge_bases_opt(one, 1, &two, MB_POSTCLEAN);
10041018
}
10051019

10061020
/*
@@ -1078,9 +1092,12 @@ struct commit_list *reduce_heads(struct commit_list *heads)
10781092
p->item->object.flags &= ~STALE;
10791093
}
10801094
}
1081-
num_head = remove_redundant(array, num_head);
1095+
mark_redundant(array, num_head);
10821096
for (i = 0; i < num_head; i++)
1083-
tail = &commit_list_insert(array[i], tail)->next;
1097+
if (!(array[i]->object.flags & STALE))
1098+
tail = &commit_list_insert(array[i], tail)->next;
1099+
else
1100+
array[i]->object.flags &= ~STALE;
10841101
return result;
10851102
}
10861103

commit.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,11 @@ extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *r
253253
extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos);
254254
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
255255

256-
/* To be used only when object flags after this call no longer matter */
257-
extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n, struct commit **twos);
256+
#define MB_POSTCLEAN 01
257+
#define MB_FPCHAIN 02
258+
extern struct commit_list *get_merge_bases_opt(struct commit *one, int n, struct commit **twos, unsigned flags);
259+
260+
#define get_merge_bases_many_dirty(one, n, twos) get_merge_bases_opt((one),(n),(twos),MB_POSTCLEAN)
258261

259262
/* largest positive number a signed 32-bit integer can contain */
260263
#define INFINITE_DEPTH 0x7fffffff

object.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ struct object_array {
3636
* bisect.c: 16
3737
* bundle.c: 16
3838
* http-push.c: 16-----19
39-
* commit.c: 16-----19
40-
* sha1_name.c: 20
39+
* commit.c: 16-------20
4140
*/
4241
#define FLAG_BITS 27
4342

sha1_name.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "remote.h"
99
#include "dir.h"
1010
#include "sha1-array.h"
11+
#include "revision.h"
1112

1213
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
1314

@@ -972,9 +973,6 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned l
972973
* For future extension, all other sequences beginning with ':/!' are reserved.
973974
*/
974975

975-
/* Remember to update object flag allocation in object.h */
976-
#define ONELINE_SEEN (1u<<20)
977-
978976
static int handle_one_ref(const char *path, const struct object_id *oid,
979977
int flag, void *cb_data)
980978
{
@@ -1016,15 +1014,15 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
10161014
return -1;
10171015

10181016
for (l = list; l; l = l->next) {
1019-
l->item->object.flags |= ONELINE_SEEN;
1017+
l->item->object.flags |= TMP_MARK;
10201018
commit_list_insert(l->item, &backup);
10211019
}
10221020
while (list) {
10231021
const char *p, *buf;
10241022
struct commit *commit;
10251023
int matches;
10261024

1027-
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
1025+
commit = pop_most_recent_commit(&list, TMP_MARK);
10281026
if (!parse_object(commit->object.oid.hash))
10291027
continue;
10301028
buf = get_commit_buffer(commit, NULL);
@@ -1041,7 +1039,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
10411039
regfree(&regex);
10421040
free_commit_list(list);
10431041
for (l = backup; l; l = l->next)
1044-
clear_commit_marks(l->item, ONELINE_SEEN);
1042+
clear_commit_marks(l->item, TMP_MARK);
10451043
free_commit_list(backup);
10461044
return found ? 0 : -1;
10471045
}

0 commit comments

Comments
 (0)