git
/
xdiff-interface.c
341 строка · 7.8 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "gettext.h"5#include "config.h"6#include "hex.h"7#include "object-store-ll.h"8#include "strbuf.h"9#include "xdiff-interface.h"10#include "xdiff/xtypes.h"11#include "xdiff/xdiffi.h"12#include "xdiff/xutils.h"13
14struct xdiff_emit_state {15xdiff_emit_hunk_fn hunk_fn;16xdiff_emit_line_fn line_fn;17void *consume_callback_data;18struct strbuf remainder;19};20
21static int xdiff_out_hunk(void *priv_,22long old_begin, long old_nr,23long new_begin, long new_nr,24const char *func, long funclen)25{
26struct xdiff_emit_state *priv = priv_;27
28if (priv->remainder.len)29BUG("xdiff emitted hunk in the middle of a line");30
31priv->hunk_fn(priv->consume_callback_data,32old_begin, old_nr, new_begin, new_nr,33func, funclen);34return 0;35}
36
37static int consume_one(void *priv_, char *s, unsigned long size)38{
39struct xdiff_emit_state *priv = priv_;40char *ep;41while (size) {42unsigned long this_size;43int ret;44ep = memchr(s, '\n', size);45this_size = (ep == NULL) ? size : (ep - s + 1);46ret = priv->line_fn(priv->consume_callback_data, s, this_size);47if (ret)48return ret;49size -= this_size;50s += this_size;51}52return 0;53}
54
55static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)56{
57struct xdiff_emit_state *priv = priv_;58int i;59int stop = 0;60
61if (!priv->line_fn)62return 0;63
64for (i = 0; i < nbuf; i++) {65if (stop)66return 1;67if (mb[i].ptr[mb[i].size-1] != '\n') {68/* Incomplete line */69strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);70continue;71}72
73/* we have a complete line */74if (!priv->remainder.len) {75stop = consume_one(priv, mb[i].ptr, mb[i].size);76continue;77}78strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);79stop = consume_one(priv, priv->remainder.buf, priv->remainder.len);80strbuf_reset(&priv->remainder);81}82if (stop)83return -1;84if (priv->remainder.len) {85stop = consume_one(priv, priv->remainder.buf, priv->remainder.len);86strbuf_reset(&priv->remainder);87}88if (stop)89return -1;90return 0;91}
92
93/*
94* Trim down common substring at the end of the buffers,
95* but end on a complete line.
96*/
97static void trim_common_tail(mmfile_t *a, mmfile_t *b)98{
99const int blk = 1024;100long trimmed = 0, recovered = 0;101char *ap = a->size ? a->ptr + a->size : a->ptr;102char *bp = b->size ? b->ptr + b->size : b->ptr;103long smaller = (a->size < b->size) ? a->size : b->size;104
105while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) {106trimmed += blk;107ap -= blk;108bp -= blk;109}110
111while (recovered < trimmed)112if (ap[recovered++] == '\n')113break;114a->size -= trimmed - recovered;115b->size -= trimmed - recovered;116}
117
118int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *xecb)119{
120mmfile_t a = *mf1;121mmfile_t b = *mf2;122
123if (mf1->size > MAX_XDIFF_SIZE || mf2->size > MAX_XDIFF_SIZE)124return -1;125
126if (!xecfg->ctxlen && !(xecfg->flags & XDL_EMIT_FUNCCONTEXT))127trim_common_tail(&a, &b);128
129return xdl_diff(&a, &b, xpp, xecfg, xecb);130}
131
132int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,133xdiff_emit_hunk_fn hunk_fn,134xdiff_emit_line_fn line_fn,135void *consume_callback_data,136xpparam_t const *xpp, xdemitconf_t const *xecfg)137{
138int ret;139struct xdiff_emit_state state;140xdemitcb_t ecb;141
142memset(&state, 0, sizeof(state));143state.hunk_fn = hunk_fn;144state.line_fn = line_fn;145state.consume_callback_data = consume_callback_data;146memset(&ecb, 0, sizeof(ecb));147if (hunk_fn)148ecb.out_hunk = xdiff_out_hunk;149ecb.out_line = xdiff_outf;150ecb.priv = &state;151strbuf_init(&state.remainder, 0);152ret = xdi_diff(mf1, mf2, xpp, xecfg, &ecb);153strbuf_release(&state.remainder);154return ret;155}
156
157int read_mmfile(mmfile_t *ptr, const char *filename)158{
159struct stat st;160FILE *f;161size_t sz;162
163if (stat(filename, &st))164return error_errno("Could not stat %s", filename);165if (!(f = fopen(filename, "rb")))166return error_errno("Could not open %s", filename);167sz = xsize_t(st.st_size);168ptr->ptr = xmalloc(sz ? sz : 1);169if (sz && fread(ptr->ptr, sz, 1, f) != 1) {170fclose(f);171return error("Could not read %s", filename);172}173fclose(f);174ptr->size = sz;175return 0;176}
177
178void read_mmblob(mmfile_t *ptr, const struct object_id *oid)179{
180unsigned long size;181enum object_type type;182
183if (oideq(oid, null_oid())) {184ptr->ptr = xstrdup("");185ptr->size = 0;186return;187}188
189ptr->ptr = repo_read_object_file(the_repository, oid, &type, &size);190if (!ptr->ptr || type != OBJ_BLOB)191die("unable to read blob object %s", oid_to_hex(oid));192ptr->size = size;193}
194
195#define FIRST_FEW_BYTES 8000196int buffer_is_binary(const char *ptr, unsigned long size)197{
198if (FIRST_FEW_BYTES < size)199size = FIRST_FEW_BYTES;200return !!memchr(ptr, 0, size);201}
202
203struct ff_regs {204int nr;205struct ff_reg {206regex_t re;207int negate;208} *array;209};210
211static long ff_regexp(const char *line, long len,212char *buffer, long buffer_size, void *priv)213{
214struct ff_regs *regs = priv;215regmatch_t pmatch[2];216int i;217int result;218
219/* Exclude terminating newline (and cr) from matching */220if (len > 0 && line[len-1] == '\n') {221if (len > 1 && line[len-2] == '\r')222len -= 2;223else224len--;225}226
227for (i = 0; i < regs->nr; i++) {228struct ff_reg *reg = regs->array + i;229if (!regexec_buf(®->re, line, len, 2, pmatch, 0)) {230if (reg->negate)231return -1;232break;233}234}235if (regs->nr <= i)236return -1;237i = pmatch[1].rm_so >= 0 ? 1 : 0;238line += pmatch[i].rm_so;239result = pmatch[i].rm_eo - pmatch[i].rm_so;240if (result > buffer_size)241result = buffer_size;242while (result > 0 && (isspace(line[result - 1])))243result--;244memcpy(buffer, line, result);245return result;246}
247
248void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)249{
250int i;251struct ff_regs *regs;252
253xecfg->find_func = ff_regexp;254regs = xecfg->find_func_priv = xmalloc(sizeof(struct ff_regs));255for (i = 0, regs->nr = 1; value[i]; i++)256if (value[i] == '\n')257regs->nr++;258ALLOC_ARRAY(regs->array, regs->nr);259for (i = 0; i < regs->nr; i++) {260struct ff_reg *reg = regs->array + i;261const char *ep, *expression;262char *buffer = NULL;263
264if (!value)265BUG("mismatch between line count and parsing");266ep = strchr(value, '\n');267
268reg->negate = (*value == '!');269if (reg->negate && i == regs->nr - 1)270die("Last expression must not be negated: %s", value);271if (*value == '!')272value++;273if (ep)274expression = buffer = xstrndup(value, ep - value);275else276expression = value;277if (regcomp(®->re, expression, cflags))278die("Invalid regexp to look for hunk header: %s", expression);279free(buffer);280value = ep ? ep + 1 : NULL;281}282}
283
284void xdiff_clear_find_func(xdemitconf_t *xecfg)285{
286if (xecfg->find_func) {287int i;288struct ff_regs *regs = xecfg->find_func_priv;289
290for (i = 0; i < regs->nr; i++)291regfree(®s->array[i].re);292free(regs->array);293free(regs);294xecfg->find_func = NULL;295xecfg->find_func_priv = NULL;296}297}
298
299unsigned long xdiff_hash_string(const char *s, size_t len, long flags)300{
301return xdl_hash_record(&s, s + len, flags);302}
303
304int xdiff_compare_lines(const char *l1, long s1,305const char *l2, long s2, long flags)306{
307return xdl_recmatch(l1, s1, l2, s2, flags);308}
309
310int parse_conflict_style_name(const char *value)311{
312if (!strcmp(value, "diff3"))313return XDL_MERGE_DIFF3;314else if (!strcmp(value, "zdiff3"))315return XDL_MERGE_ZEALOUS_DIFF3;316else if (!strcmp(value, "merge"))317return 0;318/*319* Please update _git_checkout() in git-completion.bash when
320* you add new merge config
321*/
322else323return -1;324}
325
326int git_xmerge_style = -1;327
328int git_xmerge_config(const char *var, const char *value,329const struct config_context *ctx, void *cb)330{
331if (!strcmp(var, "merge.conflictstyle")) {332if (!value)333return config_error_nonbool(var);334git_xmerge_style = parse_conflict_style_name(value);335if (git_xmerge_style == -1)336return error(_("unknown style '%s' given for '%s'"),337value, var);338return 0;339}340return git_default_config(var, value, ctx, cb);341}
342