ll-merge: replace flag argument with options struct
Keeping track of the flag bits is proving more trouble than it's worth. Instead, use a pointer to an options struct like most similar APIs do. Callers with no special requests can pass NULL to request the default options. Cc: Bert Wesarg <bert.wesarg@googlemail.com> Cc: Avery Pennarun <apenwarr@gmail.com> Helped-by: Justin Frankel <justin@cockos.com> Helped-by: Bert Wesarg <bert.wesarg@googlemail.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jonathan Nieder committed
Aug 26, 2010 at 00:49 UTC
712516bcacacd45b4825ec649f15c3573f6fb42a
7 files changed
+104
-66
Documentation/technical/api-merge.txt
+51
-20
@@ -17,6 +17,40 @@ responsible for a few things.
17
path-specific merge drivers (specified in `.gitattributes`)
18
into account.
19
20
+Data structures
21
+---------------
22
+
23
+* `mmbuffer_t`, `mmfile_t`
24
+
25
+These store data usable for use by the xdiff backend, for writing and
26
+for reading, respectively. See `xdiff/xdiff.h` for the definitions
27
+and `diff.c` for examples.
28
+
29
+* `struct ll_merge_options`
30
+
31
+This describes the set of options the calling program wants to affect
32
+the operation of a low-level (single file) merge. Some options:
33
+
34
+`virtual_ancestor`::
35
+ Behave as though this were part of a merge between common
36
+ ancestors in a recursive merge.
37
+ If a helper program is specified by the
38
+ `[merge "<driver>"] recursive` configuration, it will
39
+ be used (see linkgit:gitattributes[5]).
40
+
41
+`variant`::
42
+ Resolve local conflicts automatically in favor
43
+ of one side or the other (as in 'git merge-file'
44
+ `--ours`/`--theirs`/`--union`). Can be `0`,
45
+ `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
46
+ `XDL_MERGE_FAVOR_UNION`.
47
+
48
+`renormalize`::
49
+ Resmudge and clean the "base", "theirs" and "ours" files
50
+ before merging. Use this when the merge is likely to have
51
+ overlapped with a change in smudge/clean or end-of-line
52
+ normalization rules.
53
+
54
Low-level (single file) merge
55
-----------------------------
56
@@ -28,15 +62,24 @@ Low-level (single file) merge
62
`.git/info/attributes` into account. Returns 0 for a
63
clean merge.
64
31
-The caller:
65
+Calling sequence:
66
33
-1. allocates an mmbuffer_t variable for the result;
34
-2. allocates and fills variables with the file's original content
35
- and two modified versions (using `read_mmfile`, for example);
36
-3. calls ll_merge();
37
-4. reads the output from result_buf.ptr and result_buf.size;
38
-5. releases buffers when finished (free(ancestor.ptr); free(ours.ptr);
39
- free(theirs.ptr); free(result_buf.ptr);).
67
+* Prepare a `struct ll_merge_options` to record options.
68
+ If you have no special requests, skip this and pass `NULL`
69
+ as the `opts` parameter to use the default options.
70
+
71
+* Allocate an mmbuffer_t variable for the result.
72
+
73
+* Allocate and fill variables with the file's original content
74
+ and two modified versions (using `read_mmfile`, for example).
75
+
76
+* Call `ll_merge()`.
77
+
78
+* Read the merged content from `result_buf.ptr` and `result_buf.size`.
79
+
80
+* Release buffers when finished. A simple
81
+ `free(ancestor.ptr); free(ours.ptr); free(theirs.ptr);
82
+ free(result_buf.ptr);` will do.
83
84
If the modifications do not merge cleanly, `ll_merge` will return a
85
nonzero value and `result_buf` will generally include a description of
@@ -47,18 +90,6 @@ The `ancestor_label`, `our_label`, and `their_label` parameters are
90
used to label the different sides of a conflict if the merge driver
91
supports this.
92
50
-The `flag` parameter is a bitfield:
51
-
52
- - The `LL_OPT_VIRTUAL_ANCESTOR` bit indicates whether this is an
53
- internal merge to consolidate ancestors for a recursive merge.
54
-
55
- - The `LL_OPT_FAVOR_MASK` bits allow local conflicts to be automatically
56
- resolved in favor of one side or the other (as in 'git merge-file'
57
- `--ours`/`--theirs`/`--union`).
58
- They can be populated by `create_ll_flag`, whose argument can be
59
- `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
60
- `XDL_MERGE_FAVOR_UNION`.
61
-
93
Everything else
94
---------------
95
builtin/checkout.c
+1
-1
@@ -155,7 +155,7 @@ static int checkout_merged(int pos, struct checkout *state)
155
* merge.renormalize set, too
156
*/
157
status = ll_merge(&result_buf, path, &ancestor, "base",
158
- &ours, "ours", &theirs, "theirs", 0);
158
+ &ours, "ours", &theirs, "theirs", NULL);
159
free(ancestor.ptr);
160
free(ours.ptr);
161
free(theirs.ptr);
ll-merge.c
+33
-17
@@ -18,7 +18,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
18
mmfile_t *orig, const char *orig_name,
19
mmfile_t *src1, const char *name1,
20
mmfile_t *src2, const char *name2,
21
- int flag,
21
+ const struct ll_merge_options *opts,
22
int marker_size);
23
24
struct ll_merge_driver {
@@ -39,14 +39,18 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
39
mmfile_t *orig, const char *orig_name,
40
mmfile_t *src1, const char *name1,
41
mmfile_t *src2, const char *name2,
42
- int flag, int marker_size)
42
+ const struct ll_merge_options *opts,
43
+ int marker_size)
44
{
45
+ mmfile_t *stolen;
46
+ assert(opts);
47
+
48
/*
49
* The tentative merge result is "ours" for the final round,
50
* or common ancestor for an internal merge. Still return
51
* "conflicted merge" status.
52
*/
49
- mmfile_t *stolen = (flag & LL_OPT_VIRTUAL_ANCESTOR) ? orig : src1;
53
+ stolen = opts->virtual_ancestor ? orig : src1;
54
55
result->ptr = stolen->ptr;
56
result->size = stolen->size;
@@ -60,9 +64,11 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
64
mmfile_t *orig, const char *orig_name,
65
mmfile_t *src1, const char *name1,
66
mmfile_t *src2, const char *name2,
63
- int flag, int marker_size)
67
+ const struct ll_merge_options *opts,
68
+ int marker_size)
69
{
70
xmparam_t xmp;
71
+ assert(opts);
72
73
if (buffer_is_binary(orig->ptr, orig->size) ||
74
buffer_is_binary(src1->ptr, src1->size) ||
@@ -74,12 +80,12 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
80
orig, orig_name,
81
src1, name1,
82
src2, name2,
77
- flag, marker_size);
83
+ opts, marker_size);
84
}
85
86
memset(&xmp, 0, sizeof(xmp));
87
xmp.level = XDL_MERGE_ZEALOUS;
82
- xmp.favor = ll_opt_favor(flag);
88
+ xmp.favor = opts->variant;
89
if (git_xmerge_style >= 0)
90
xmp.style = git_xmerge_style;
91
if (marker_size > 0)
@@ -96,15 +102,17 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
102
mmfile_t *orig, const char *orig_name,
103
mmfile_t *src1, const char *name1,
104
mmfile_t *src2, const char *name2,
99
- int flag, int marker_size)
105
+ const struct ll_merge_options *opts,
106
+ int marker_size)
107
{
108
/* Use union favor */
102
- flag &= ~LL_OPT_FAVOR_MASK;
103
- flag |= create_ll_flag(XDL_MERGE_FAVOR_UNION);
109
+ struct ll_merge_options o;
110
+ assert(opts);
111
+ o = *opts;
112
+ o.variant = XDL_MERGE_FAVOR_UNION;
113
return ll_xdl_merge(drv_unused, result, path_unused,
114
orig, NULL, src1, NULL, src2, NULL,
106
- flag, marker_size);
107
- return 0;
115
+ &o, marker_size);
116
}
117
118
#define LL_BINARY_MERGE 0
@@ -136,7 +144,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
144
mmfile_t *orig, const char *orig_name,
145
mmfile_t *src1, const char *name1,
146
mmfile_t *src2, const char *name2,
139
- int flag, int marker_size)
147
+ const struct ll_merge_options *opts,
148
+ int marker_size)
149
{
150
char temp[4][50];
151
struct strbuf cmd = STRBUF_INIT;
@@ -144,6 +153,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
153
const char *args[] = { NULL, NULL };
154
int status, fd, i;
155
struct stat st;
156
+ assert(opts);
157
158
dict[0].placeholder = "O"; dict[0].value = temp[0];
159
dict[1].placeholder = "A"; dict[1].value = temp[1];
@@ -337,15 +347,21 @@ int ll_merge(mmbuffer_t *result_buf,
347
mmfile_t *ancestor, const char *ancestor_label,
348
mmfile_t *ours, const char *our_label,
349
mmfile_t *theirs, const char *their_label,
340
- int flag)
350
+ const struct ll_merge_options *opts)
351
{
352
static struct git_attr_check check[2];
353
const char *ll_driver_name = NULL;
354
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
355
const struct ll_merge_driver *driver;
346
- int virtual_ancestor = flag & LL_OPT_VIRTUAL_ANCESTOR;
356
348
- if (flag & LL_OPT_RENORMALIZE) {
357
+ if (!opts) {
358
+ struct ll_merge_options default_opts = {0};
359
+ return ll_merge(result_buf, path, ancestor, ancestor_label,
360
+ ours, our_label, theirs, their_label,
361
+ &default_opts);
362
+ }
363
+
364
+ if (opts->renormalize) {
365
normalize_file(ancestor, path);
366
normalize_file(ours, path);
367
normalize_file(theirs, path);
@@ -359,11 +375,11 @@ int ll_merge(mmbuffer_t *result_buf,
375
}
376
}
377
driver = find_ll_merge_driver(ll_driver_name);
362
- if (virtual_ancestor && driver->recursive)
378
+ if (opts->virtual_ancestor && driver->recursive)
379
driver = find_ll_merge_driver(driver->recursive);
380
return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
381
ours, our_label, theirs, their_label,
366
- flag, marker_size);
382
+ opts, marker_size);
383
}
384
385
int ll_merge_marker_size(const char *path)
ll-merge.h
+6
-15
@@ -5,27 +5,18 @@
5
#ifndef LL_MERGE_H
6
#define LL_MERGE_H
7
8
-#define LL_OPT_VIRTUAL_ANCESTOR (1 << 0)
9
-#define LL_OPT_FAVOR_MASK ((1 << 1) | (1 << 2))
10
-#define LL_OPT_FAVOR_SHIFT 1
11
-#define LL_OPT_RENORMALIZE (1 << 3)
12
-
13
-static inline int ll_opt_favor(int flag)
14
-{
15
- return (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;
16
-}
17
-
18
-static inline int create_ll_flag(int favor)
19
-{
20
- return ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);
21
-}
8
+struct ll_merge_options {
9
+ unsigned virtual_ancestor : 1;
10
+ unsigned variant : 2; /* favor ours, favor theirs, or union merge */
11
+ unsigned renormalize : 1;
12
+};
13
14
int ll_merge(mmbuffer_t *result_buf,
15
const char *path,
16
mmfile_t *ancestor, const char *ancestor_label,
17
mmfile_t *ours, const char *our_label,
18
mmfile_t *theirs, const char *their_label,
28
- int flag);
19
+ const struct ll_merge_options *opts);
20
21
int ll_merge_marker_size(const char *path);
22
merge-file.c
+1
-1
@@ -37,7 +37,7 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
37
* common ancestor.
38
*/
39
merge_status = ll_merge(&res, path, base, NULL,
40
- our, ".our", their, ".their", 0);
40
+ our, ".our", their, ".their", NULL);
41
if (merge_status < 0)
42
return NULL;
43
merge-recursive.c
+11
-11
@@ -608,22 +608,25 @@ static int merge_3way(struct merge_options *o,
608
const char *branch2)
609
{
610
mmfile_t orig, src1, src2;
611
+ struct ll_merge_options ll_opts = {0};
612
char *base_name, *name1, *name2;
613
int merge_status;
613
- int favor;
614
615
- if (o->call_depth)
616
- favor = 0;
617
- else {
615
+ ll_opts.renormalize = o->renormalize;
616
+
617
+ if (o->call_depth) {
618
+ ll_opts.virtual_ancestor = 1;
619
+ ll_opts.variant = 0;
620
+ } else {
621
switch (o->recursive_variant) {
622
case MERGE_RECURSIVE_OURS:
620
- favor = XDL_MERGE_FAVOR_OURS;
623
+ ll_opts.variant = XDL_MERGE_FAVOR_OURS;
624
break;
625
case MERGE_RECURSIVE_THEIRS:
623
- favor = XDL_MERGE_FAVOR_THEIRS;
626
+ ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
627
break;
628
default:
626
- favor = 0;
629
+ ll_opts.variant = 0;
630
break;
631
}
632
}
@@ -646,10 +649,7 @@ static int merge_3way(struct merge_options *o,
649
read_mmblob(&src2, b->sha1);
650
651
merge_status = ll_merge(result_buf, a->path, &orig, base_name,
649
- &src1, name1, &src2, name2,
650
- ((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |
651
- (o->renormalize ? LL_OPT_RENORMALIZE : 0) |
652
- create_ll_flag(favor)));
652
+ &src1, name1, &src2, name2, &ll_opts);
653
654
free(name1);
655
free(name2);
rerere.c
+1
-1
@@ -325,7 +325,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
325
*/
326
ll_merge(&result, path, &mmfile[0], NULL,
327
&mmfile[1], "ours",
328
- &mmfile[2], "theirs", 0);
328
+ &mmfile[2], "theirs", NULL);
329
for (i = 0; i < 3; i++)
330
free(mmfile[i].ptr);
331