* (unknown) @ 2006-11-21 22:24 Johannes Schindelin 2006-11-21 23:15 ` [PATCH] xdiff: add xdl_merge() (was: (unknown)) Jakub Narebski ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Johannes Schindelin @ 2006-11-21 22:24 UTC (permalink / raw) To: Davide Libenzi, git [PATCH] xdiff: add xdl_merge() This new function implements the functionality of RCS merge, but in-memory. It returns < 0 on error, otherwise the number of conflicts. Finding the conflicting lines can be a very expensive task. You can control the eagerness of this algorithm: - a level value of 0 means that all overlapping changes are treated as conflicts, - a value of 1 means that if the overlapping changes are identical, it is not treated as a conflict. - If you set level to 2, overlapping changes will be analyzed, so that almost identical changes will not result in huge conflicts. Rather, only the conflicting lines will be shown inside conflict markers. With each increasing level, the algorithm gets slower, but more accurate. Note that the code for level 2 depends on the simple definition of mmfile_t specific to git, and therefore it will be harder to port that to LibXDiff. Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de> --- This code is only lightly tested, but I am tired and hungry. My hopes are that when I wake up in the morning, all bugs are fixed, git-merge-one-file is rewritten as a builtin, git-merge-index defaults to calling xdl_merge() directly when no program is passed with "-o", and git-merge-recursive also avoids fork()ing RCS' merge. A funny side effect is that you can merge with white space corruption by setting the xpparam flags to ignore whitespace. The file passed as mf1 wins over mf2 in that case. Oh, and maybe I did not need to use xdl_change_compact() (and thus could have left that static to xdiffi.c)? Davide, if you could enlighten me, I will take the time next weekend to port this to LibXDiff ;-) BTW if anybody wonders why these diffstats do not look correct: "git apply --stat" does not yet have our new scaling... Makefile | 3 xdiff/xdiff.h | 7 + xdiff/xdiffi.c | 3 xdiff/xdiffi.h | 1 xdiff/xmerge.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 444 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 4c3f87f..b10d65e 100644 --- a/Makefile +++ b/Makefile @@ -769,7 +769,8 @@ $(DIFF_OBJS): diffcore.h $(LIB_FILE): $(LIB_OBJS) rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) -XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o +XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \ + xdiff/xmerge.o $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \ xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h index c9f8178..fa409d5 100644 --- a/xdiff/xdiff.h +++ b/xdiff/xdiff.h @@ -49,6 +49,9 @@ extern "C" { #define XDL_BDOP_CPY 2 #define XDL_BDOP_INSB 3 +#define XDL_MERGE_MINIMAL 0 +#define XDL_MERGE_EAGER 1 +#define XDL_MERGE_ZEALOUS 2 typedef struct s_mmfile { char *ptr; @@ -90,6 +93,10 @@ long xdl_mmfile_size(mmfile_t *mmf); int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb); +int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, + mmfile_t *mf2, const char *name2, + xpparam_t const *xpp, int level, mmbuffer_t *result); + #ifdef __cplusplus } #endif /* #ifdef __cplusplus */ diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index d76e76a..9aeebc4 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -45,7 +45,6 @@ static long xdl_split(unsigned long cons long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, xdalgoenv_t *xenv); static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2); -static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags); @@ -397,7 +396,7 @@ static xdchange_t *xdl_add_change(xdchan } -static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { +int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec; char *rchg = xdf->rchg, *rchgo = xdfo->rchg; xrecord_t **recs = xdf->recs; diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h index d3b7271..472aeae 100644 --- a/xdiff/xdiffi.h +++ b/xdiff/xdiffi.h @@ -50,6 +50,7 @@ int xdl_recs_cmp(diffdata_t *dd1, long o long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv); int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *xe); +int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags); int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr); void xdl_free_script(xdchange_t *xscr); int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c new file mode 100644 index 0000000..7b85aa5 --- /dev/null +++ b/xdiff/xmerge.c @@ -0,0 +1,433 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#include "xinclude.h" + +typedef struct s_xdmerge { + struct s_xdmerge *next; + /* + * 0 = conflict, + * 1 = no conflict, take first, + * 2 = no conflict, take second. + */ + int mode; + long i1, i2; + long chg1, chg2; +} xdmerge_t; + +static int xdl_append_merge(xdmerge_t **merge, int mode, + long i1, long chg1, long i2, long chg2) +{ + xdmerge_t *m = *merge; + if (m && mode == m->mode && + (i1 == m->i1 + m->chg1 || i2 == m->i2 + m->chg2)) { + m->chg1 = i1 + chg1 - m->i1; + m->chg2 = i2 + chg2 - m->i2; + } else { + m = xdl_malloc(sizeof(xdmerge_t)); + if (!m) + return -1; + m->next = NULL; + m->mode = mode; + m->i1 = i1; + m->chg1 = chg1; + m->i2 = i2; + m->chg2 = chg2; + if (*merge) + (*merge)->next = m; + *merge = m; + } + return 0; +} + +static int xdl_cleanup_merge(xdmerge_t *c) +{ + int count = 0; + xdmerge_t *next_c; + + /* were there conflicts? */ + for (; c; c = next_c) { + if (c->mode == 0) + count++; + next_c = c->next; + free(c); + } + return count; +} + +static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2, + int line_count, long flags) +{ + int i; + xrecord_t **rec1 = xe1->xdf2.recs + i1; + xrecord_t **rec2 = xe2->xdf2.recs + i2; + + for (i = 0; i < line_count; i++) { + int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size, + rec2[i]->ptr, rec2[i]->size, flags); + if (!result) + return -1; + } + return 0; +} + +static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest) +{ + xrecord_t **recs = xe->xdf2.recs + i; + int size = 0; + + if (count < 1) + return 0; + + for (i = 0; i < count; size += recs[i++]->size) + if (dest) + memcpy(dest + size, recs[i]->ptr, recs[i]->size); + if (add_nl) { + i = recs[count - 1]->size; + if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') { + if (dest) + dest[size] = '\n'; + size++; + } + } + return size; +} + +static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, + xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest) +{ + const int marker_size = 7; + int marker1_size = (name1 ? strlen(name1) + 1 : 0); + int marker2_size = (name2 ? strlen(name2) + 1 : 0); + int conflict_marker_size = 3 * (marker_size + 1) + + marker1_size + marker2_size; + int size, i1, j; + + for (size = i1 = 0; m; m = m->next) { + if (m->mode == 0) { + size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0, + dest ? dest + size : NULL); + if (dest) { + for (j = 0; j < marker_size; j++) + dest[size++] = '<'; + if (marker1_size) { + dest[size] = ' '; + memcpy(dest + size + 1, name1, + marker1_size - 1); + size += marker1_size; + } + dest[size++] = '\n'; + } else + size += conflict_marker_size; + size += xdl_recs_copy(xe1, m->i1, m->chg1, 1, + dest ? dest + size : NULL); + if (dest) { + for (j = 0; j < marker_size; j++) + dest[size++] = '='; + dest[size++] = '\n'; + } + size += xdl_recs_copy(xe2, m->i2, m->chg2, 1, + dest ? dest + size : NULL); + if (dest) { + for (j = 0; j < marker_size; j++) + dest[size++] = '>'; + if (marker2_size) { + dest[size] = ' '; + memcpy(dest + size + 1, name2, + marker2_size - 1); + size += marker2_size; + } + dest[size++] = '\n'; + } + } else if (m->mode == 1) + size += xdl_recs_copy(xe1, i1, m->i1 + m->chg1 - i1, 0, + dest ? dest + size : NULL); + else if (m->mode == 2) + size += xdl_recs_copy(xe2, m->i2 - m->i1 + i1, + m->i1 + m->chg2 - i1, 0, + dest ? dest + size : NULL); + i1 = m->i1 + m->chg1; + } + size += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0, + dest ? dest + size : NULL); + return size; +} + +/* + * Sometimes, changes are not quite identical, but differ in only a few + * lines. Try hard to show only these few lines as conflicting. + */ +static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m, + xpparam_t const *xpp) +{ + for (; m; m = m->next) { + mmfile_t t1, t2; + xdfenv_t xe; + xdchange_t *xscr, *x; + int i1 = m->i1, i2 = m->i2; + + /* let's handle just the conflicts */ + if (m->mode) + continue; + + /* + * This probably does not work outside git, since + * we have a very simple mmfile structure. + */ + t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr; + t1.size = xe1->xdf2.recs[m->i1 + m->chg1]->ptr + + xe1->xdf2.recs[m->i1 + m->chg1]->size - t1.ptr; + t2.ptr = (char *)xe2->xdf2.recs[m->i1]->ptr; + t2.size = xe2->xdf2.recs[m->i1 + m->chg1]->ptr + + xe2->xdf2.recs[m->i1 + m->chg1]->size - t2.ptr; + if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0) + return -1; + if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 || + xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 || + xdl_build_script(&xe, &xscr) < 0) { + xdl_free_env(&xe); + return -1; + } + if (!xscr) { + /* If this happens, it's a bug. */ + xdl_free_env(&xe); + return -2; + } + x = xscr; + m->i1 = xscr->i1 + i1; + m->chg1 = xscr->chg1; + m->i2 = xscr->i2 + i2; + m->chg2 = xscr->chg2; + while (xscr->next) { + xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t)); + if (!m2) { + xdl_free_env(&xe); + xdl_free_script(x); + return -1; + } + xscr = xscr->next; + m2->next = m->next; + m->next = m2; + m = m2; + m->mode = 0; + m->i1 = xscr->i1 + i1; + m->chg1 = xscr->chg1; + m->i2 = xscr->i2 + i2; + m->chg2 = xscr->chg2; + } + xdl_free_env(&xe); + xdl_free_script(x); + } + return 0; +} + +/* + * level == 0: mark all overlapping changes as conflict + * level == 1: mark overlapping changes as conflict only if not identical + * level == 2: analyze non-identical changes for minimal conflict set + * + * returns < 0 on error, == 0 for no conflicts, else number of conflicts + */ +static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1, + xdfenv_t *xe2, xdchange_t *xscr2, const char *name2, + int level, xpparam_t const *xpp, mmbuffer_t *result) { + xdmerge_t *changes, *c; + int i1, i2, chg1, chg2; + + c = changes = NULL; + + while (xscr1 && xscr2) { + if (!changes) + changes = c; + if (xscr1->i1 + xscr1->chg1 < xscr2->i1) { + i1 = xscr1->i2; + i2 = xscr2->i2 - xscr2->i1 + xscr1->i1; + chg1 = xscr1->chg2; + chg2 = xscr1->chg1; + if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) { + xdl_cleanup_merge(changes); + return -1; + } + xscr1 = xscr1->next; + continue; + } + if (xscr2->i1 + xscr2->chg1 < xscr1->i1) { + i1 = xscr1->i2 - xscr1->i1 + xscr2->i1; + i2 = xscr2->i2; + chg1 = xscr2->chg1; + chg2 = xscr2->chg2; + if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) { + xdl_cleanup_merge(changes); + return -1; + } + xscr2 = xscr2->next; + continue; + } + if (level < 1 || xscr1->i1 != xscr2->i1 || + xscr1->chg1 != xscr2->chg1 || + xscr1->chg2 != xscr2->chg2 || + xdl_merge_cmp_lines(xe1, xscr1->i2, + xe2, xscr2->i2, + xscr1->chg2, xpp->flags)) { + /* conflict */ + int off = xscr1->i1 - xscr2->i1; + int ffo = off + xscr1->chg1 - xscr2->chg1; + + i1 = xscr1->i2; + i2 = xscr2->i2; + if (off > 0) + i1 -= off; + else + i2 += off; + chg1 = xscr1->i2 + xscr1->chg2 - i1; + chg2 = xscr2->i2 + xscr2->chg2 - i2; + if (ffo > 0) + chg2 += ffo; + else + chg1 -= ffo; + if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) { + xdl_cleanup_merge(changes); + return -1; + } + } + + i1 = xscr1->i1 + xscr1->chg1; + i2 = xscr2->i1 + xscr2->chg1; + + if (i1 > i2) { + xscr1->chg1 -= i1 - i2; + xscr1->i1 = i2; + xscr1->i2 += xscr1->chg2; + xscr1->chg2 = 0; + xscr1 = xscr1->next; + } else if (i2 > i1) { + xscr2->chg1 -= i2 - i1; + xscr2->i1 = i1; + xscr2->i2 += xscr2->chg2; + xscr2->chg2 = 0; + xscr2 = xscr2->next; + } else { + xscr1 = xscr1->next; + xscr2 = xscr2->next; + } + } + while (xscr1) { + if (!changes) + changes = c; + i1 = xscr1->i2; + i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec; + chg1 = xscr1->chg2; + chg2 = xscr1->chg1; + if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) { + xdl_cleanup_merge(changes); + return -1; + } + xscr1 = xscr1->next; + } + while (xscr2) { + if (!changes) + changes = c; + i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec; + i2 = xscr2->i2; + chg1 = xscr2->chg1; + chg2 = xscr2->chg2; + if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) { + xdl_cleanup_merge(changes); + return -1; + } + xscr2 = xscr2->next; + } + if (!changes) + changes = c; + /* refine conflicts */ + if (level > 1 && xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0) { + xdl_cleanup_merge(changes); + return -1; + } + /* output */ + if (result) { + int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2, + changes, NULL); + result->ptr = xdl_malloc(size); + if (!result->ptr) { + xdl_cleanup_merge(changes); + return -1; + } + result->size = size; + xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes, + result->ptr); + } + return xdl_cleanup_merge(changes); +} + +int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, + mmfile_t *mf2, const char *name2, + xpparam_t const *xpp, int level, mmbuffer_t *result) { + xdchange_t *xscr1, *xscr2; + xdfenv_t xe1, xe2; + + result->ptr = NULL; + result->size = 0; + + if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 || + xdl_do_diff(orig, mf2, xpp, &xe2) < 0) { + return -1; + } + if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 || + xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 || + xdl_build_script(&xe1, &xscr1) < 0) { + xdl_free_env(&xe1); + return -1; + } + if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 || + xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 || + xdl_build_script(&xe2, &xscr2) < 0) { + xdl_free_env(&xe2); + return -1; + } + if (xscr1 || xscr2) { + if (!xscr1) { + result->ptr = xdl_malloc(mf2->size); + memcpy(result->ptr, mf2->ptr, mf2->size); + result->size = mf2->size; + } else if (!xscr2) { + result->ptr = xdl_malloc(mf1->size); + memcpy(result->ptr, mf1->ptr, mf1->size); + result->size = mf1->size; + } else if (xdl_do_merge(&xe1, xscr1, name1, + &xe2, xscr2, name2, + level, xpp, result) < 0) { + xdl_free_script(xscr1); + xdl_free_script(xscr2); + xdl_free_env(&xe1); + xdl_free_env(&xe2); + return -1; + } + xdl_free_script(xscr1); + xdl_free_script(xscr2); + } + xdl_free_env(&xe1); + xdl_free_env(&xe2); + + return 0; +} ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [PATCH] xdiff: add xdl_merge() (was: (unknown)) 2006-11-21 22:24 (unknown) Johannes Schindelin @ 2006-11-21 23:15 ` Jakub Narebski 2006-11-22 9:29 ` Johannes Schindelin 2006-11-22 19:58 ` [PATCH] xdiff: add xdl_merge() Ramsay Jones 2006-11-22 20:16 ` your mail Davide Libenzi 2 siblings, 1 reply; 34+ messages in thread From: Jakub Narebski @ 2006-11-21 23:15 UTC (permalink / raw) To: git Johannes Schindelin wrote: > [PATCH] xdiff: add xdl_merge() Shouldn't this be in the subject of message? > This new function implements the functionality of RCS merge, but > in-memory. It returns < 0 on error, otherwise the number of conflicts. Only RCS merge, or can you implement whole diff3 (from GNU diffutils) functionality with that? > Finding the conflicting lines can be a very expensive task. You can > control the eagerness of this algorithm: > > - a level value of 0 means that all overlapping changes are treated > as conflicts, > - a value of 1 means that if the overlapping changes are identical, > it is not treated as a conflict. > - If you set level to 2, overlapping changes will be analyzed, so that > almost identical changes will not result in huge conflicts. Rather, > only the conflicting lines will be shown inside conflict markers. > > With each increasing level, the algorithm gets slower, but more accurate. > Note that the code for level 2 depends on the simple definition of > mmfile_t specific to git, and therefore it will be harder to port that > to LibXDiff. How it compares performance with RCS merge/GNU diff3? It is really nice to have that. Bram Cohen (of Codeville, SCM built around sophisticated merge algorithm) wrote about recursive three-way merge in http://revctrl.org/CrissCrossMerge Recursive three-way merge _usually_ provides the right answer, however there are some edge cases. For example, conflict markers can be matched incorrectly, because they aren't given any special semantic meaning for the merge algorithm, and are simply treated as lines. In particular, there are (somewhat complicated) cases where the conflict markers of two unrelated conflicts get matched against each other, even though the content sections of them are totally unrelated. I'm not sure if he has specific examples, or is it just theoretical talk, but having built-in merge would certainly help revursive merge strategy (and perhaps also git-rerere). -- Jakub Narebski Warsaw, Poland ShadeHawk on #git ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH] xdiff: add xdl_merge() (was: (unknown)) 2006-11-21 23:15 ` [PATCH] xdiff: add xdl_merge() (was: (unknown)) Jakub Narebski @ 2006-11-22 9:29 ` Johannes Schindelin 0 siblings, 0 replies; 34+ messages in thread From: Johannes Schindelin @ 2006-11-22 9:29 UTC (permalink / raw) To: Jakub Narebski; +Cc: Davide Libenzi, git [-- Attachment #1: Type: TEXT/PLAIN, Size: 4033 bytes --] Hi, On Wed, 22 Nov 2006, Jakub Narebski wrote: > Johannes Schindelin wrote: > > > [PATCH] xdiff: add xdl_merge() > > Shouldn't this be in the subject of message? Did I mention that I was tired and hungry? > > This new function implements the functionality of RCS merge, but > > in-memory. It returns < 0 on error, otherwise the number of conflicts. > > Only RCS merge, or can you implement whole diff3 (from GNU diffutils) > functionality with that? As I am interested only in the in-memory merge, only RCS merge. Which feature would you be interested in? An ed script? :-) > > Finding the conflicting lines can be a very expensive task. You can > > control the eagerness of this algorithm: > > > > - a level value of 0 means that all overlapping changes are treated > > as conflicts, > > - a value of 1 means that if the overlapping changes are identical, > > it is not treated as a conflict. > > - If you set level to 2, overlapping changes will be analyzed, so that > > almost identical changes will not result in huge conflicts. Rather, > > only the conflicting lines will be shown inside conflict markers. > > > > With each increasing level, the algorithm gets slower, but more accurate. > > Note that the code for level 2 depends on the simple definition of > > mmfile_t specific to git, and therefore it will be harder to port that > > to LibXDiff. > > How it compares performance with RCS merge/GNU diff3? Speedwise, I have no clue. It was enough work for a day. Accuracywise: often I sent a patch (series) which was in my current git tree (no topic branch), and Junio did some minor adjustments. I _hated_ the fact that RCS merge marked _all_ overlapping changes as conflicts, even when there was just a minor correction here and there. And "git diff --ours" does not help at all. Here is where my implementation should help. With level 2, it will look again at these conflicting regions, and only output the actual differences as conflicts. > It is really nice to have that. Bram Cohen (of Codeville, SCM built around > sophisticated merge algorithm) wrote about recursive three-way merge in > http://revctrl.org/CrissCrossMerge > > Recursive three-way merge _usually_ provides the right answer, however > there are some edge cases. For example, conflict markers can be matched > incorrectly, because they aren't given any special semantic meaning for > the merge algorithm, and are simply treated as lines. In particular, > there are (somewhat complicated) cases where the conflict markers of two > unrelated conflicts get matched against each other, even though the > content sections of them are totally unrelated. > > I'm not sure if he has specific examples, or is it just theoretical talk, > but having built-in merge would certainly help revursive merge strategy > (and perhaps also git-rerere). It should be easy to construct such an example. However, the relevance in practice is about zero. Git was built from the beginning to aim to do a merge as good as possible, but not perfect. There is no such thing as a perfect merge algorithm. You will always be able to construct cases which are mismerged. Thus, git takes the pragmatic approach and stops "early": merges work in 99% of the time, and in 99% of the remaining 1% the merge will fail so that you know you have to fix it manually. (Take these numbers with a grain of salt, please.) The advantage of stopping there is that we can make it really fast. You could probably raise the 99% to 99.5%, by implementing a "rebasing merge", i.e. cherry-picking the branch-to-be-merged committing only in the end (if there has not been any conflict). Obviously, this is slow as Parnell's pitch[1]. As for git-rerere: I could not use it everywhere, because of some Perl dependencies which I could not compile on some platforms. However, IMHO git-rerere does not necessarily depend on merge being available in libgit. Ciao, Dscho Footnote 1: http://www.physics.uq.edu.au/pitchdrop/pitchdrop.shtml ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH] xdiff: add xdl_merge() 2006-11-21 22:24 (unknown) Johannes Schindelin 2006-11-21 23:15 ` [PATCH] xdiff: add xdl_merge() (was: (unknown)) Jakub Narebski @ 2006-11-22 19:58 ` Ramsay Jones 2006-11-22 20:16 ` your mail Davide Libenzi 2 siblings, 0 replies; 34+ messages in thread From: Ramsay Jones @ 2006-11-22 19:58 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Davide Libenzi, git, Junio C Hamano [-- Attachment #1: Type: text/plain, Size: 3583 bytes --] Hi Johannes, Johannes Schindelin wrote: > This new function implements the functionality of RCS merge, but > in-memory. It returns < 0 on error, otherwise the number of conflicts. > I had a similar idea (removing the RCS dependency) and, about two months ago, implemented a git-diff3 that used the internal xdiff library. Unfortunately, I then got distracted by other things and left this as a WIP, without posting it to the list. [I had an email exchange with Junio about it, but then dropped the ball - sorry Junio!] > Finding the conflicting lines can be a very expensive task. You can > control the eagerness of this algorithm: > > - a level value of 0 means that all overlapping changes are treated > as conflicts, > - a value of 1 means that if the overlapping changes are identical, > it is not treated as a conflict. > - If you set level to 2, overlapping changes will be analyzed, so that > almost identical changes will not result in huge conflicts. Rather, > only the conflicting lines will be shown inside conflict markers. > > With each increasing level, the algorithm gets slower, but more accurate. > Note that the code for level 2 depends on the simple definition of > mmfile_t specific to git, and therefore it will be harder to port that > to LibXDiff. > Erm, I guess I need to read the code! I haven't had an opportunity to do that yet, but I hope to have time at the weekend. > My hopes are that when I wake up in the morning, all bugs are > fixed, git-merge-one-file is rewritten as a builtin, > git-merge-index defaults to calling xdl_merge() directly when > no program is passed with "-o", and git-merge-recursive also > avoids fork()ing RCS' merge. Yep, I had a similar todo list. > > A funny side effect is that you can merge with white space > corruption by setting the xpparam flags to ignore whitespace. > The file passed as mf1 wins over mf2 in that case. Yes, I had this on my todo list. In fact you can already try it out by using --diff-program="diff -wb" (which uses the external diff). [To be fair, you can also write an obvious script and use GNU diff3] > > Makefile | 3 > xdiff/xdiff.h | 7 + > xdiff/xdiffi.c | 3 > xdiff/xdiffi.h | 1 > xdiff/xmerge.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 444 insertions(+), 3 deletions(-) > From the above, I would guess that your code integrates well with the xdiff library and, as a result, would be more memory efficient than my code. I had considered doing this, but I wanted to support an external diff option, so it was easier to implement the diff3 algorithm outside the library, treating it just like an external diff (that I didn't need to fork()/exec()). Also, I didn't need to understand the xdiff code, I just treat it as a black box (and I'm lazy!). [Having an external diff option was actually a debugging aid to ensure that the main diff3 algorithm was implemented correctly. In the end I didn't use it for debugging anyway!] [snip patch] I have attached a version of my patch, against v1.4.2, just for the interested. Also it contains some tests which I found very helpful in squashing bugs, so you might like to give them a try with your code. Note: the fact that the patch is against v1.4.2 should not cause too much of a problem, since it is mostly new files and the changes to Makefile, builtin.h and git.c you could probably do in your sleep. [Junio, this is almost exactly the same patch I sent you last time, modulo some minor code clean-up/formatting] All the best, Ramsay [-- Attachment #2: p0016.txt --] [-- Type: text/plain, Size: 52987 bytes --] >From f23d8df5d7774cb85b834357019c725cf8c1e231 Mon Sep 17 00:00:00 2001 From: Ramsay Jones <ramsay@ramsay1.demon.co.uk> Date: Mon, 13 Nov 2006 23:04:37 +0000 Subject: [PATCH] Initial implementation of builtin diff3 The intention of this work is to remove the dependancy on the rcs merge program. As a first step, we implement a builtin clone of diff3. (A builtin merge replacement command will be able to reuse this code, since merge is a thin wrapper around diff3). So git-diff3 is only a stepping stone to a merge replacement and is not meant to stay! Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk> --- Makefile | 5 builtin-diff3.c | 1824 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 2 git.c | 1 t/txxxx-diff3.sh | 175 +++++ t/txxxx/lao | 11 t/txxxx/lao1 | 8 t/txxxx/lao2 | 16 t/txxxx/lao3 | 13 t/txxxx/lao4 | 12 t/txxxx/tao | 14 t/txxxx/tao1 | 10 t/txxxx/tao2 | 16 t/txxxx/tao3 | 13 t/txxxx/tao4 | 15 t/txxxx/tzu | 13 16 files changed, 2146 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0761d6c..60f5adc 100644 --- a/Makefile +++ b/Makefile @@ -198,7 +198,7 @@ BUILT_INS = git-log$X git-whatchanged$X git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \ git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \ git-fmt-merge-msg$X git-prune$X git-mv$X git-prune-packed$X \ - git-repo-config$X + git-repo-config$X git-diff3$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -255,7 +255,8 @@ BUILTIN_OBJS = \ builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \ builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \ builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \ - builtin-mv.o builtin-prune-packed.o builtin-repo-config.o + builtin-mv.o builtin-prune-packed.o builtin-repo-config.o \ + builtin-diff3.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-diff3.c b/builtin-diff3.c new file mode 100644 index 0000000..9f5698f --- /dev/null +++ b/builtin-diff3.c @@ -0,0 +1,1824 @@ +/* + * Builtin "git diff3" + * + */ +#include "cache.h" +#include "builtin.h" +#include "xdiff-interface.h" +#include <sys/wait.h> +#include <assert.h> + + +#define MAX_DIFF_ARGS 32 + +/* diff3 options */ +struct diff3_options { + unsigned show_all:1; /* show all changes, including merged */ + unsigned overlap_only:1; /* restrict to overlapping changes */ + unsigned unmerged_only:1;/* restrict to unmerged non-overlapping */ + unsigned initial_tab:1; /* output initial tab */ + unsigned markers:1; /* output conflict markers */ + unsigned edscript:1; /* output an ed script to perform the merge */ + unsigned merge:1; /* actually output the merged file */ + const char *diff_prog; /* external diff program (command line) */ +}; + +/* input file info */ +struct finfo { + const char *name; /* filename */ + const char *label; /* label to use for markers */ + int num; /* 0 = ours, 1 = base, 2 = theirs */ + mmfile_t mf; /* file mmap() info */ +}; + +/* pipe info when running external diff */ +struct pinfo { + int fd; /* pipe read/write fd */ + pid_t pid; /* child pid to wait for */ +}; + +/* argv info for external diff */ +struct diff_argv { + char *args; /* argument string memory */ + const char **argv; /* argument pointer array */ + int argc; /* argument count */ +}; + +/* callback info used with internal diff */ +struct ecb_info { + char *text; /* pointer to text of diff output */ + long size; /* size of memory block holding text */ + long used; /* length of diff output */ +}; + +/* type of diff */ +enum { + UNKNOWN_FORMAT, + NORMAL_FORMAT, + UNIFIED_FORMAT, + CONTEXT_FORMAT +}; + +/* normal format diff hunk header */ +struct hunk_header { + int fs, fe; /* from-file start and end line numbers */ + int type; /* 'a' add, 'c' change, 'd' delete */ + int ts, te; /* to-file start and end line numbers */ +}; + +/* unified format diff hunk header */ +struct unified_hunk_header { + int fs, fc; /* from-file start line and count */ + int ts, tc; /* to-file start line and count */ +}; + +/* lines from one file referenced by a hunk */ +struct hunk_lines { + int start; /* start line number of hunk */ + int end; /* end line number of hunk */ + char **lines; /* pointer to array of line pointers */ + int *lengths; /* pointer to array of line lengths */ + int size; /* size of the above arrays */ +}; + +/* 2way diff hunk */ +struct diff2_hunk { + int type; /* hunk type: 'a', 'c', 'd' */ + struct hunk_lines from; /* from-file hunk lines */ + struct hunk_lines to; /* to-file hunk lines */ + struct diff2_hunk *next; /* next hunk in list */ +}; + +/* 2way diff */ +struct diff2 { + struct finfo *ff; /* from file */ + struct finfo *ft; /* to file */ + mmbuffer_t content; /* text content of diff */ + struct diff2_hunk *hunks; /* list of diff hunks */ +}; + +/* a segment (sub-list) of diff2 hunks */ +struct segment { + int from; /* from-file number */ + int to; /* to-file number */ + struct diff2_hunk *head; /* head of hunk list segment */ + struct diff2_hunk *tail; /* tail of hunk list segment */ + int count; /* count of hunks */ +}; + +/* 3way diff hunk */ +struct diff3_hunk { + int type; /* 'a' all, '1' 1st, '2' 2nd, '3' 3rd */ + struct hunk_lines f[3]; /* hunk lines for files 0, 1, 2 */ + struct diff3_hunk *next; /* next hunk in list */ +}; + +/* 3way diff */ +struct diff3 { + struct diff2 *diff01; /* diff ours -> base */ + struct diff2 *diff21; /* diff theirs -> base */ + struct diff3_hunk *hunks; /* list of diff3 hunks */ +}; + +static void init_finfo(struct finfo *f, const char *name, const char *lab, int num) +{ + if (f) { + f->name = name; + f->label = lab; + f->num = num; + + f->mf.ptr = 0; + f->mf.size = 0; + } +} + +static void alloc_line_arrays(int num, struct hunk_lines *h) +{ + assert(h); + h->lines = xcalloc(num, sizeof(char *)); + h->lengths = xcalloc(num, sizeof(int)); + h->size = num; +} + +static struct diff2_hunk *new_diff2_hunk(void) +{ + struct diff2_hunk *new = xmalloc(sizeof(*new)); + + memset(new, 0, sizeof(*new)); + + return new; +} + +static void free_diff2_hunk(struct diff2_hunk *p) +{ + if (p->from.lines) + free(p->from.lines); + if (p->from.lengths) + free(p->from.lengths); + + if (p->to.lines) + free(p->to.lines); + if (p->to.lengths) + free(p->to.lengths); + + free(p); +} + +static struct diff2 *new_diff2(struct finfo *ff, struct finfo *ft) +{ + struct diff2 *new = xmalloc(sizeof(*new)); + + memset(new, 0, sizeof(*new)); + + new->ff = ff; + new->ft = ft; + + return new; +} + +static void free_diff2(struct diff2 *p) +{ + struct diff2_hunk *h, *next; + + for (h = p->hunks; h; h = next) { + next = h->next; + free_diff2_hunk(h); + } + + if (p->content.ptr) + free(p->content.ptr); + + free(p); +} + +static struct diff3_hunk *new_diff3_hunk(void) +{ + struct diff3_hunk *new = xmalloc(sizeof(*new)); + + memset(new, 0, sizeof(*new)); + + return new; +} + +static void free_diff3_hunk(struct diff3_hunk *p) +{ + int i; + + for (i = 0; i < 3; i++) { + if (p->f[i].lines) + free(p->f[i].lines); + if (p->f[i].lengths) + free(p->f[i].lengths); + } + + free(p); +} + +static struct diff3 *new_diff3(struct diff2 *diff01, struct diff2 *diff21) +{ + struct diff3 *new = xmalloc(sizeof(*new)); + + memset(new, 0, sizeof(*new)); + + new->diff01 = diff01; + new->diff21 = diff21; + + return new; +} + +static void free_diff3(struct diff3 *p) +{ + struct diff3_hunk *h, *next; + + for (h = p->hunks; h; h = next) { + next = h->next; + free_diff3_hunk(h); + } + + free_diff2(p->diff01); + free_diff2(p->diff21); + + free(p); +} + +static int init_mmfile(mmfile_t *mf, const char *file) +{ + char *data; + long size; + struct stat st; + int fd; + + assert(mf && file && *file); + + mf->ptr = 0; + mf->size = 0; + + if (lstat(file, &st) < 0) + return -1; + size = st.st_size; + if (!size) { + mf->ptr = ""; + return 0; + } + + fd = open(file, O_RDONLY); + if (fd < 0) + return -1; + data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (data == MAP_FAILED) + return -1; + + mf->ptr = data; + mf->size = size; + return 0; +} + +static void free_mmfile(mmfile_t *mf) +{ + if (mf) { + if (mf->ptr && mf->size != 0) + munmap(mf->ptr, mf->size); + mf->ptr = 0; + mf->size = 0; + } +} + +static void map_file(struct finfo *f) +{ + if (!f->mf.ptr && (init_mmfile(&f->mf, f->name) < 0)) + die("can't read file '%s': %s", f->name, strerror(errno)); +} + +static int popenv(int argc, const char *argv[], const char *type, struct pinfo *p) +{ + int pfd[2]; + pid_t pid; + int fd; + + if (!argv || !type || !p) { + errno = EINVAL; + return -1; + } + p->fd = -1; + p->pid = 0; + + if (!*type || (*type != 'r' && *type != 'w') || type[1] != 0) { + errno = EINVAL; + return -1; + } + + if (pipe(pfd) < 0) + return -1; + + if ((pid = fork()) < 0) { + return -1; + } else if (pid == 0) { /* child */ + if (*type == 'r') { + close(pfd[0]); + if(pfd[1] != STDOUT_FILENO) { + dup2(pfd[1], STDOUT_FILENO); + close(pfd[1]); + } + } else { /* *type == 'w' */ + close(pfd[1]); + if(pfd[0] != STDIN_FILENO) { + dup2(pfd[0], STDIN_FILENO); + close(pfd[0]); + } + + } + +#ifdef DUMMY + fd = open("/dev/null", O_RDWR); + dup2(fd, STDERR_FILENO); + close(fd); +#endif + + execvp(argv[0], (char *const *)argv); + _exit(127); + } + + /* parent */ + if (*type == 'r') { + close(pfd[1]); + fd = pfd[0]; + } else { + close(pfd[0]); + fd = pfd[1]; + } + + p->fd = fd; + p->pid = pid; + + return 0; +} + +static int pclosev(struct pinfo *p) +{ + int stat; + + if (!p || p->fd == -1 || p->pid == 0) { + errno = EINVAL; + return -1; + } + + close(p->fd); /* XXX check for close() failure? */ + p->fd = -1; + + while (waitpid(p->pid, &stat, 0) < 0) + if (errno != EINTR) + return -1; + + p->pid = 0; + + return stat; +} + +static int split_cmdline(const char *cmdline, int max, struct diff_argv *prog) +{ + assert(prog); + + if (cmdline && *cmdline && (prog->args = xmalloc(strlen(cmdline)+2))) { + + const char *p = cmdline; + char *a = prog->args; + int argc = 0; + + prog->argv = xmalloc(max * sizeof(char *)); + + while( *p ) { + + while (*p == ' ' || *p == '\t') + p++; + if ( !*p ) + break; + + if (argc >= max) + break; + + prog->argv[argc++] = a; + while ((*a = *p) && *a != ' ' && *a != '\t') + a++,p++; + *a++ = 0; + + } + + *a++ = 0; + prog->argc = argc; + prog->argv[argc] = 0; + + if (argc == 0) { + free(prog->args); + free(prog->argv); + return -1; + } + + return 0; + } + + return -1; +} + +static struct diff_argv *new_diff_argv(const char *cmdline) +{ + struct diff_argv *a = xmalloc(sizeof(*a)); + + memset(a, 0, sizeof(*a)); + + if (split_cmdline(cmdline, MAX_DIFF_ARGS, a) < 0) { + free(a); + return 0; + } + + return a; +} + +static void free_diff_argv(struct diff_argv *a) +{ + if (a) { + if (a->args) + free(a->args); + if (a->argv) + free(a->argv); + free(a); + } +} + +static int read_from_pipe(int fd, mmbuffer_t *content) +{ + ssize_t n, size = 1024, total = 0; + char *text = xmalloc(size); + + assert(content != 0); + + while ((n = read(fd, text+total, size-total)) > 0) { + if (n == size-total) { + size *= 2; + text = xrealloc(text, size); + } + total += n; + } + if (n < 0) + return -1; + + if (total == 0) + free(text); + else { + content->ptr = text; + content->size = total; + } + + return 0; +} + +static void run_external_diff(struct diff_argv *prog, const char* file1, const char* file2, mmbuffer_t *content) +{ + char const **ap; + struct pinfo pi; + int stat, code; + + if (!prog || !prog->args || !prog->argv || prog->argc < 1) + die("internal error: invalid diff argv"); + + if (prog->argc > MAX_DIFF_ARGS - 4) + die("too many arguments for external diff-program"); + + ap = &prog->argv[prog->argc]; + *ap++ = "--"; + *ap++ = file1; + *ap++ = file2; + *ap++ = 0; + + if(popenv(prog->argc+4, prog->argv, "r", &pi) < 0) + die("popenv() failed: %s", strerror(errno)); + + if (read_from_pipe(pi.fd, content) < 0) + die("read_from_pipe() failed: %s", strerror(errno)); + + if ((stat = pclosev(&pi)) < 0) + die("pclosev() failed: %s", strerror(errno)); + + if (!WIFEXITED(stat)) + die("diff-program '%s' terminated abnormally", prog->argv[0]); + + if ((code = WEXITSTATUS(stat)) == 127) + die("can't execute external diff-program '%s'", prog->argv[0]); + + if (code < 0 || code > 1) + die("external diff-program '%s' failed", prog->argv[0]); +} + +#ifdef DUMMY +static void print_buffer(mmbuffer_t *b) +{ + ssize_t count = 0, n; + + if (b->size == 0) + return; + + while ((n = xwrite(STDOUT_FILENO, b->ptr+count, b->size-count)) > 0) + count += n; +} +#endif + +static void external_diff(struct diff_argv *prog, struct finfo *ff, struct finfo *ft, mmbuffer_t *diff) +{ + assert(ff != 0 && ft != 0 && diff != 0); + + run_external_diff(prog, ff->name, ft->name, diff); + +#ifdef DUMMY + printf("diff %s %s\n", ff->name, ft->name); + print_buffer(diff); + printf("--------\n"); +#endif +} + +static int xdiff_output(void *priv_, mmbuffer_t *mb, int nbuf) +{ + struct ecb_info *b = priv_; + int i; + + for (i = 0; i < nbuf; i++) { + + if (b->used + mb[i].size > b->size) { + if (b->size > mb[i].size) + b->size *= 2; + else + b->size += mb[i].size; + b->text = xrealloc(b->text, b->size); + } + + memcpy(b->text+b->used, mb[i].ptr, mb[i].size); + b->used += mb[i].size; + + } + return 0; +} + +static void run_internal_diff(mmfile_t *mf1, mmfile_t *mf2, mmbuffer_t *diff) +{ + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + struct ecb_info ecb_data; + + ecb_data.text = xmalloc(1024); + ecb_data.size = 1024; + ecb_data.used = 0; + + xpp.flags = XDF_NEED_MINIMAL; + + xecfg.ctxlen = 0; + xecfg.flags = 0; + + ecb.outf = xdiff_output; + ecb.priv = &ecb_data; + + xdl_diff(mf1, mf2, &xpp, &xecfg, &ecb); + + if (ecb_data.used == 0) + free(ecb_data.text); + else { + diff->ptr = ecb_data.text; + diff->size = ecb_data.used; + } +} + +static void internal_diff(struct finfo *ff, struct finfo *ft, mmbuffer_t *diff) +{ + assert(ff != 0 && ft != 0 && diff != 0); + + map_file(ff); + map_file(ft); + + run_internal_diff(&ff->mf, &ft->mf, diff); + +#ifdef DUMMY + printf("diff %s %s\n", ff->name, ft->name); + print_buffer(diff); + printf("--------\n"); +#endif +} + + +static void read_diff(struct diff_argv *prog, struct finfo *ff, struct finfo *ft, mmbuffer_t *diff) +{ + if (prog) + external_diff(prog, ff, ft, diff); + else + internal_diff(ff, ft, diff); +} + +static char *nextline(char *p, char *limit) +{ + while (p < limit) + if (*p++ == '\n') + return p; + return 0; +} + +static int get_num(char **tp, int *np) +{ + char *p = *tp; + int neg = 0; + int num = 0; + + /* can't use: num = strtol(p, tp, 10); because strtol() will + * skip using isspace() which includes '\n'. + */ + + while (*p == ' ' || *p == '\t') p++; + + if (*p == '-' || *p == '+') { + neg = *p == '-'; + p++; + } + + while (isdigit(*p)) + num = num * 10 + (*p++ - '0'); + + if (*tp == p) + return 0; + + *tp = p; + *np = (neg) ? -num : num; + return 1; +} + +static int have(char **tp, char c) +{ + char *p = *tp; + + while (*p == ' ' || *p == '\t') p++; + + if (*p == c) { + *tp = ++p; + return 1; + } + + return 0; +} + +static int at_eol(char **tp) +{ + char *p = *tp; + + while (*p == ' ' || *p == '\t') p++; + *tp = p; + + if (*p == '\n') + return 1; + + return 0; +} + +static int normal_header(char **curp, struct hunk_header *header) +{ + char *p = *curp; + + if (!get_num(&p, &header->fs)) + return 0; + + header->fe = header->fs; + if (have(&p, ',')) { + if(!get_num(&p, &header->fe)) + return 0; + } + + if (have(&p, 'a')) + header->type = 'a'; + else if (have(&p, 'c')) + header->type = 'c'; + else if (have(&p, 'd')) + header->type = 'd'; + else + return 0; + + if (header->type == 'a') { + assert(header->fs == header->fe); + header->fe--; /* null (zero) range of lines */ + } + + if (!get_num(&p, &header->ts)) + return 0; + + header->te = header->ts; + if (have(&p, ',')) { + if(!get_num(&p, &header->te)) + return 0; + } + + if (header->type == 'd') { + assert(header->ts == header->te); + header->te--; /* null (zero) range of lines */ + } + + if(!at_eol(&p)) + return 0; + + *curp = ++p; + return 1; +} + +static int determine_diff_format(mmbuffer_t *content, char** curp) +{ + char *p = content->ptr; + char *limit = p + content->size; + char *p2, *p3, *p4; + struct hunk_header head; + + *curp = p; + + if((p2 = nextline(p, limit)) == 0) + return UNKNOWN_FORMAT; + + /* internal (unified) diff, no header */ + if (memcmp(p, "@@ ", 3) == 0) + return UNIFIED_FORMAT; + + /* normal format diff */ + if (normal_header(&p, &head)) + return NORMAL_FORMAT; + + if((p3 = nextline(p2, limit)) == 0) + return UNKNOWN_FORMAT; + + /* external (unified) diff, with header */ + if (memcmp(p, "--- ", 4) == 0 && + memcmp(p2, "+++ ", 4) == 0 && + memcmp(p3, "@@ ", 3) == 0) { + + *curp = p3; + return UNIFIED_FORMAT; + } + + /* context diff */ + if (memcmp(p, "*** ", 4) == 0 && + memcmp(p2, "--- ", 4) == 0 && + memcmp(p3, "****", 4) == 0) { + + if((p4 = nextline(p3, limit)) == 0) + return UNKNOWN_FORMAT; + + *curp = p4; + return CONTEXT_FORMAT; + } + + return UNKNOWN_FORMAT; +} + +static void get_line(char **curp, char *limit, char type, int num, struct hunk_lines *hl) +{ + char *p = *curp; + int normal = (type == '<') || (type == '>'); + char *pn; + + assert(p < limit); + + if (*p++ != type || (normal && *p++ != ' ')) + die("invalid diff: leading line chars"); + + hl->lines[num] = p; + + if ((pn = nextline(p, limit)) == 0) + die("invalid diff: missing eol #3"); + + hl->lengths[num] = (int)(pn - p); + + /* check for "\ No newline at end of file" */ + if (pn < limit && *pn == '\\') { + /* remove nl from length and skip */ + hl->lengths[num] = (int)(pn - p) - 1; + pn = nextline(pn, limit); + } + + *curp = pn; +} + +static void process_normal_diff(struct diff2 *d, char *p) +{ + mmbuffer_t *content = &d->content; + char *limit = content->ptr + content->size; + struct diff2_hunk *hunk, **last_hunk; + int i; + + last_hunk = &d->hunks; + + while (p < limit) { + struct hunk_header head; + + hunk = new_diff2_hunk(); + + /* hunk header: <int>[,<int>]<type><int>[,<int>] */ + if (!normal_header(&p, &head)) + die("invalid diff: header"); + + hunk->type = head.type; + + /* from-file hunk lines (if any) */ + hunk->from.start = head.fs; + hunk->from.end = head.fe; + if (head.type != 'a') { + int numlines = head.fe - head.fs + 1; + + assert(numlines > 0); + + alloc_line_arrays(numlines, &hunk->from); + + for (i = 0; i < numlines; i++) + get_line(&p, limit, '<', i, &hunk->from); + } + + /* skip change hunk separator (if any) */ + if (head.type == 'c') { + if (memcmp(p, "---\n", 4) != 0) + die("invalid diff: missing change separator"); + p += 4; + } + + /* to-file hunk lines (if any) */ + hunk->to.start = head.ts; + hunk->to.end = head.te; + if (head.type != 'd') { + int numlines = head.te - head.ts + 1; + + assert(numlines > 0); + + alloc_line_arrays(numlines, &hunk->to); + + for (i = 0; i < numlines; i++) + get_line(&p, limit, '>', i, &hunk->to); + } + + /* add this hunk to the end of the list */ + *last_hunk = hunk; + last_hunk = &hunk->next; + + } + + *last_hunk = 0; +} + +static int have_str(char **tp, char *s) +{ + char *p = *tp; + int len = strlen(s); + + if (memcmp(p, s, len) == 0) { + p += len; + *tp = p; + return 1; + } + + return 0; +} + +static int unified_header(char **curp, struct unified_hunk_header *header) +{ + char *p = *curp; + + if (!have_str(&p, "@@ -")) + return 0; + + if (!get_num(&p, &header->fs)) + return 0; + + header->fc = 1; + if (have(&p, ',')) { + if(!get_num(&p, &header->fc)) + return 0; + } + + if (!have_str(&p, " +")) + return 0; + + if (!get_num(&p, &header->ts)) + return 0; + + header->tc = 1; + if (have(&p, ',')) { + if(!get_num(&p, &header->tc)) + return 0; + } + + if (!have_str(&p, " @@")) + return 0; + + if(!at_eol(&p)) + return 0; + + *curp = ++p; + return 1; +} + +static int count_hunk_lines(char *p, char *limit, char type) +{ + int num = 0; + + while (p < limit && *p == type) { + char *pn; + + if ((pn = nextline(p, limit)) == 0) + die("invalid diff: missing eol #1"); + + num++; + p = pn; + } + + return num; +} + +static void process_unified_diff(struct diff2 *d, char *p) +{ + mmbuffer_t *content = &d->content; + char *limit = content->ptr + content->size; + struct diff2_hunk *hunk, **last_hunk; + int i; + + last_hunk = &d->hunks; + + while (p < limit) { + struct unified_hunk_header head; + int single_hunk = 0; + + /* hunk header: @@ -<int>[,<int>] +<int>[,<int>] @@ */ + if (!unified_header(&p, &head)) + die("invalid diff: header"); + + single_hunk = (head.fc == 0 || head.tc == 0); + + for (;;) { /* for all hunks */ + int cc = 0, fc = 0, tc = 0; + + /* find begining of next hunk: skip common lines */ + while (p < limit && *p == ' ') { + char *pn; + + cc++; + head.fs++; head.fc--; + head.ts++; head.tc--; + + if ((pn = nextline(p, limit)) == 0) + die("invalid diff: missing eol #2"); + p = pn; + } + + /* finished? */ + if (limit <= p || *p == '@') { + assert(head.fc == 0 && head.tc == 0); + break; + } + if (*p != '-' && *p != '+') + die("invalid diff: missing -/+ lines"); + + hunk = new_diff2_hunk(); + + hunk->from.start = hunk->from.end = head.fs; + hunk->to.start = hunk->to.end = head.ts; + + /* from-file hunk lines (if any) */ + fc = count_hunk_lines(p, limit, '-'); + if (fc > 0) { + alloc_line_arrays(fc, &hunk->from); + + for (i = 0; i < fc; i++) + get_line(&p, limit, '-', i, &hunk->from); + + hunk->from.end += fc-1; + + head.fs += fc; + head.fc -= fc; + } + + /* to-file hunk lines (if any) */ + tc = count_hunk_lines(p, limit, '+'); + if (tc > 0) { + alloc_line_arrays(tc, &hunk->to); + + for (i = 0; i < tc; i++) + get_line(&p, limit, '+', i, &hunk->to); + + hunk->to.end += tc-1; + + head.ts += tc; + head.tc -= tc; + } + + /* set hunk type */ + assert(fc > 0 || tc > 0); + if (fc == 0) { + hunk->type = 'a'; + if (!single_hunk) + hunk->from.start--; + hunk->from.end = hunk->from.start - 1; + } else if (tc == 0) { + hunk->type = 'd'; + if (!single_hunk) + hunk->to.start--; + hunk->to.end = hunk->to.start - 1; + } else { + hunk->type = 'c'; + } + + /* add this hunk to the end of the list */ + *last_hunk = hunk; + last_hunk = &hunk->next; + } + + } + + *last_hunk = 0; + +} + +struct diff2* process_diff(struct diff_argv *prog, struct finfo *ff, struct finfo *ft) +{ + struct diff2 *result = new_diff2(ff, ft); + char *scan; + + read_diff(prog, ff, ft, &result->content); + + if (result->content.size == 0) + return result; /* empty diff is ok */ + + assert(result->content.size > 0); + + if (result->content.ptr[result->content.size-1] != '\n') + die("invalid diff: missing eol on last line"); + + switch (determine_diff_format(&result->content, &scan)) { + case NORMAL_FORMAT: + process_normal_diff(result, scan); + break; + case UNIFIED_FORMAT: + process_unified_diff(result, scan); + break; + case CONTEXT_FORMAT: + die("context diff format not supported"); + break; + case UNKNOWN_FORMAT: + default: + die("unknown diff format"); + break; + } + + return result; +} + +static void adjust_diff2(struct diff2 *d) +{ + struct diff2_hunk *p; + + for (p = d->hunks; p; p = p->next) { + if (p->type == 'a') { + p->from.start++; + p->from.end++; + } else if (p->type == 'd') { + p->to.start++; + p->to.end++; + } + } +} + +static int copy_lines(struct hunk_lines *dst, int off, struct hunk_lines *src) +{ + char **s = src->lines; + int *sl = src->lengths; + char **d = dst->lines + off; + int *dl = dst->lengths + off; + int n = src->size; + + assert(n <= dst->size - off); + + while(n--) { + if (*d) { + if (*sl != *dl || memcmp(*s, *d, *sl)) { + return 0; + } + } else { + *d = *s; + *dl = *sl; + } + d++; dl++; + s++; sl++; + } + return 1; +} + +static int equal_lines(struct hunk_lines *lhs, struct hunk_lines *rhs) +{ + char **l = lhs->lines; + int *ll = lhs->lengths; + char **r = rhs->lines; + int *rl = rhs->lengths; + int n = lhs->size; + + if (n != rhs->size) + return 0; + + while (n--) { + if (!*l || !*r || *ll != *rl || memcmp(*l, *r, *ll)) + return 0; + l++; ll++; + r++; rl++; + } + return 1; +} + +static int get_low_thread(struct diff2_hunk *p[2]) +{ + if (!p[0]) + return 1; + if (!p[1]) + return 0; + return (p[0]->to.start > p[1]->to.start) ? 1 : 0; +} + +static void init_segment(struct segment *s, int from, int to) +{ + s->from = from; + s->to = to; + s->head = 0; + s->tail = 0; + s->count = 0; +} + +static void add_segment(struct segment *s, struct diff2_hunk *d) +{ + assert(d != 0); + + if (!s->head) + s->head = s->tail = d; + else + s->tail = d; + s->count++; +} + +static struct diff3_hunk *make_diff3_hunk(struct segment use[2], + int low_thread, int high_thread, struct diff3_hunk *last) +{ + struct diff3_hunk *result = new_diff3_hunk(); + int startc, endc; + int i, common; + int from0, from1; + + assert(use[0].to == use[1].to); + assert(use[0].from != use[1].from); + + from0 = use[0].from; + from1 = use[1].from; + + common = use[0].to; + assert(common >= 0 && common <= 2); + + /* find start/end line numbers from the common file */ + startc = result->f[common].start = use[low_thread].head->to.start; + endc = result->f[common].end = use[high_thread].tail->to.end; + + /* find start/end line numbers for other files */ + for (i = 0; i < 2; i++) { + struct segment *s = &use[i]; + struct diff2_hunk *h = s->head; + struct diff2_hunk *t = s->tail; + int file = s->from; + int start = startc, end = endc; + + assert(file >= 0 && file <= 2); + + if (h) { + start = startc - h->to.start + h->from.start; + end = endc - t->to.end + t->from.end; + } else if (last) { + int d = last->f[file].end - last->f[common].end; + start = startc + d; + end = endc + d; + } + result->f[file].start = start; + result->f[file].end = end; + } + + /* allocate hunk_lines line pointer and lengths arrays */ + for (i = 0; i < 3; i++) { + int numlines = result->f[i].end - result->f[i].start + 1; + + if (numlines > 0) + alloc_line_arrays(numlines, &result->f[i]); + } + + /* copy line info for common file */ + for (i = 0; i < 2; i++) { + struct segment *s = &use[i]; + struct diff2_hunk *h = s->head; + int j; + + for (j = 0; j < s->count; j++,h = h->next) { + int offset = h->to.start - startc; + + if (!copy_lines(&result->f[common], offset, &h->to)) + die("internal error: copy_lines()"); + + } + } + + /* copy line info for other files */ + for (i = 0; i < 2; i++) { + struct segment *s = &use[i]; + struct diff2_hunk *h = s->head; + struct hunk_lines *tf, *cf; + int start, end, file = s->from; + int j, k, l, m; + + assert(file >= 0 && file <= 2); + + tf = &result->f[file]; + cf = &result->f[common]; + + start = tf->start; + end = tf->end; + + /* before first hunk */ + k = (h) ? h->from.start : end+1; + for (j = 0; j + start < k; j++) { + tf->lines[j] = cf->lines[j]; + tf->lengths[j] = cf->lengths[j]; + } + + /* hunk list */ + for (j = 0; j < s->count; j++,h = h->next) { + int offset = h->from.start - start; + + if (!copy_lines(&result->f[file], offset, &h->from)) + die("internal error:"); + + /* between this and next hunk */ + l = h->to.end + 1 - startc; + if (j < s->count-1) + k = h->next->from.start - start; + else + k = end+1 - start; + for (m = h->from.end + 1 - start; m < k; m++,l++) { + tf->lines[m] = cf->lines[l]; + tf->lengths[m] = cf->lengths[l]; + } + + } + + } + + /* set diff3 hunk type */ + if (!use[0].head) + result->type = '0' + from1; + else if (!use[1].head) + result->type = '0' + from0; + else { + if (equal_lines(&result->f[from0], &result->f[from1])) + result->type = '0' + common; + else + result->type = 'a'; + } + + return result; +} + +static struct diff3 *make_3way_diff(struct diff2 *d01, struct diff2 *d21) +{ + struct diff3 *result = new_diff3(d01, d21); + struct diff2_hunk *diff01 = d01->hunks; + struct diff2_hunk *diff21 = d21->hunks; + struct diff2_hunk *p[2], *high_diff, *other_diff; + struct diff3_hunk *hunk, **last_hunk; + struct diff3_hunk *last; + struct segment use[2]; + int low_thread, high_thread; + int other_thread, high_line; + + assert(d01->ft->num == d21->ft->num); + assert(d01->ff->num != d21->ff->num); + + adjust_diff2(d01); + adjust_diff2(d21); + + last_hunk = &result->hunks; + last = 0; + p[0] = diff01; + p[1] = diff21; + + while (p[0] || p[1]) { + init_segment(&use[0], d01->ff->num, d01->ft->num); + init_segment(&use[1], d21->ff->num, d21->ft->num); + + high_thread = low_thread = get_low_thread(p); + + high_diff = p[high_thread]; + high_line = high_diff->to.end; + + add_segment(&use[high_thread], high_diff); + p[high_thread] = high_diff->next; + + other_thread = high_thread ^ 0x1; + other_diff = p[other_thread]; + + while (other_diff && other_diff->to.start <= high_line+1) { + + add_segment(&use[other_thread], other_diff); + p[other_thread] = p[other_thread]->next; + + if (high_line < other_diff->to.end) { + high_thread ^= 0x1; + high_diff = other_diff; + high_line = other_diff->to.end; + } + + other_thread = high_thread ^ 0x1; + other_diff = p[other_thread]; + } + + hunk = make_diff3_hunk(use, low_thread, high_thread, last); + + if (!hunk) + die("can't create diff3 hunk"); + + /* add this hunk to the end of the list */ + *last_hunk = hunk; + last_hunk = &hunk->next; + + last = hunk; + } + + *last_hunk = 0; + + return result; +} + +#ifdef DUMMY +static void print_diff2_hunk(struct diff2_hunk *p) +{ + struct hunk_lines *f = &p->from; + struct hunk_lines *t = &p->to; + int i, type = p->type; + + printf("%d,%d %c %d,%d\n", f->start, f->end, type, t->start, t->end); + + for (i = 0; i < f->size; i++) + printf("< %.*s", f->lengths[i], f->lines[i]); + + printf("---\n"); + + for (i = 0; i < t->size; i++) + printf("> %.*s", t->lengths[i], t->lines[i]); + + printf("+++\n"); +} + +static void print_diff2(struct diff2 *d) +{ + struct diff2_hunk *p; + + printf("<--- diff2 --->\n"); + for (p = d->hunks; p; p = p->next) + print_diff2_hunk(p); + printf(">=============<\n"); +} +#endif + +static void print_diff3_hunk(FILE *outf, struct diff3_hunk *p, char *prefix) +{ + int i, j, k, skip_print; + + if (p->type == 'a') + fprintf(outf, "====\n"); + else + fprintf(outf, "====%c\n", p->type+1); + + switch (p->type) { + case '0': skip_print = 1; break; + case '1': skip_print = 0; break; + case '2': skip_print = 0; break; + case 'a': skip_print = 3; break; + default: + die("internal error: invalid diff3 hunk type"); + break; + } + + for (j = 0,k = 0; j < 3; j++,k++) { + int numlines, start, end, len; + char *s; + + if (p->type == '1' && j > 0) + k = (j == 1) ? 2 : 1; + + start = p->f[k].start; + end = p->f[k].end; + + numlines = end - start + 1; + + if (numlines == 0) + fprintf(outf, "%d:%da\n", k+1, start-1); + else if (numlines == 1) + fprintf(outf, "%d:%dc\n", k+1, start); + else + fprintf(outf, "%d:%d,%dc\n", k+1, start, end); + + if (k == skip_print) + continue; + + assert(numlines == p->f[k].size); + + if (p->f[k].size <= 0) + continue; + + s = 0; + len = 0; + + for (i = 0; i < p->f[k].size; i++) { + s = p->f[k].lines[i]; + len = p->f[k].lengths[i]; + fprintf(outf, "%s", prefix); + fwrite(s, 1, len, outf); + } + + if (s && (len == 0 || s[len-1] != '\n')) + fprintf(outf, "\n\\ No newline at end of file\n"); + + } + +} + +static void print_diff3(FILE *outf, struct diff3 *d, struct diff3_options *o) +{ + struct diff3_hunk *p; + char *prefix = (o->initial_tab) ? "\t" : " "; + + for (p = d->hunks; p; p = p->next) + print_diff3_hunk(outf, p, prefix); +} + +static int write_lines(FILE *outf, char **curp, char *limit, int num) +{ + char *p = *curp; + char *pn; + int size; + + assert(num >= 0); + + while (num--) { + if ((pn = nextline(p, limit)) == 0) { + if (num > 0) + return -1; + pn = limit; + } + p = pn; + } + size = (int)(p - *curp); + if (size > 0) + fwrite(*curp, 1, size, outf); + *curp = p; + return 0; +} + +static int skip_lines(char **curp, char *limit, int num) +{ + char *p = *curp; + char *pn; + + assert(num >= 0); + + while (num--) { + if ((pn = nextline(p, limit)) == 0) { + if (num > 0) + return -1; + pn = limit; + } + p = pn; + } + *curp = p; + return 0; +} + +static int diff3_merge(FILE *outf, mmfile_t *inf, struct diff3 *d, struct finfo f[3], struct diff3_options *opts) +{ + struct diff3_hunk *p; + int conflicts = 0; + int inf_lines = 0; + char *infp, *inf_limit; + + infp = inf->ptr; + inf_limit = infp + inf->size; + + for (p = d->hunks; p; p = p->next) { + int conflict = 0; + int i, lines; + + /* check if we can skip this block */ + switch (p->type) { + default: + case '0': /* change unique to our file: skip */ + continue; + break; + case '1': /* same in ours and theirs: merged change */ + if (!opts->show_all) + continue; + conflict = 1; + break; + case '2': /* change only in theirs: un-merged change */ + if (opts->overlap_only) + continue; + break; + case 'a': /* all files differ: overlapping change */ + if (opts->unmerged_only) + continue; + conflict = opts->markers; + break; + } + + /* write lines from '0' (our) file (if any) */ + lines = p->f[0].start - inf_lines - 1; + if (lines > 0) { + if (write_lines(outf, &infp, inf_limit, lines) < 0) + die("unexpected eof on file"); + inf_lines += lines; + } + + if (conflict) { + /* write lines from files with conflict markers */ + char *marker_fmt = "<<<<<<< %s\n"; + + conflicts++; + + if (p->type == 'a') { + /* write lines from '0' (our) file */ + fprintf(outf, marker_fmt, f[0].label); + for (i = 0; i < p->f[0].size; i++) { + char *s = p->f[0].lines[i]; + int len = p->f[0].lengths[i]; + fwrite(s, 1, len, outf); + } + marker_fmt = "||||||| %s\n"; + } + + if (opts->show_all) { + /* write lines from '1' (base) file */ + fprintf(outf, marker_fmt, f[1].label); + for (i = 0; i < p->f[1].size; i++) { + char *s = p->f[1].lines[i]; + int len = p->f[1].lengths[i]; + fwrite(s, 1, len, outf); + } + } + + fprintf(outf, "=======\n"); + + } + + /* write lines from '2' (their) file */ + for (i = 0; i < p->f[2].size; i++) { + char *s = p->f[2].lines[i]; + int len = p->f[2].lengths[i]; + fwrite(s, 1, len, outf); + } + + /* add conflict marker */ + if (conflict) + fprintf(outf, ">>>>>>> %s\n", f[2].label); + + /* skip lines from '0' (our) file */ + lines = p->f[0].size; + if (lines > 0) { + if (skip_lines(&infp, inf_limit, lines) < 0) + die("unexpected eof"); + inf_lines += lines; + } + + } + + /* write remainder of '0' (our) file */ + if (infp < inf_limit) + fwrite(infp, 1, (int)(inf_limit - infp), outf); + + return conflicts; +} + +int diff3(FILE *outf, const char *file[3], const char *label[3], struct diff3_options *opts) +{ + int i; + struct finfo f[3]; + struct diff2 *d1, *d2; + struct diff3 *d3; + struct diff_argv *prog = 0; + int conflicts = 0; + + for ( i = 0; i < 3; i++ ) + init_finfo(&f[i], file[i], label[i], i); + + if (opts->diff_prog) + prog = new_diff_argv(opts->diff_prog); + + d2 = process_diff(prog, &f[2], &f[1]); + free_mmfile(&f[2].mf); + + d1 = process_diff(prog, &f[0], &f[1]); + free_mmfile(&f[1].mf); + + d3 = make_3way_diff(d1, d2); + + if (opts->merge) { + map_file(&f[0]); + conflicts = diff3_merge(outf, &f[0].mf, d3, f, opts); + } else { + print_diff3(outf, d3, opts); + } + + free_mmfile(&f[0].mf); + free_diff3(d3); /* also free()'s d1 and d2 */ + free_diff_argv(prog); + + return conflicts; +} + + +static const char builtin_diff3_usage[] = +"git-diff3 [options]... ours base theirs\n" +"\n" +" -e --ed output unmerged changes from BASE to THEIRS into OURS\n" +" -E --show-overlap output unmerged changes, bracketing conflicts\n" +" -A --show-all output all changes, bracketing conflicts\n" +" -x --overlap-only output overlapping changes\n" +" -X output overlapping changes, bracketing them\n" +" -3 --easy-only output unmerged non-overlapping changes\n" +"\n" +" -m --merge output merged file instead of ed script\n" +" -T --initial-tab make tabs line up by prepending a tab\n" +"\n" +" -L LABEL --label=LABEL use LABEL instead of filename\n" +" --diff-program=PROGRAM use PROGRAM to compare files\n" +; + + +int cmd_diff3(int argc, const char **argv, const char *prefix) +{ + int i; + const char *label_strings[3]; + int label_count = 0; + int conflicts = 0; + int edarg = 0; + const char **file; + struct diff3_options opts; + + memset(&opts, 0, sizeof(opts)); + + for (i = 1 ; i < argc ; i++) { + const char *arg = argv[i]; + + if (*arg != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-e") || !strcmp(arg, "--ed")) { + edarg++; + continue; + } + if (!strcmp(arg, "-E") || !strcmp(arg, "--show-overlap")) { + edarg++; + opts.markers = 1; + continue; + } + if (!strcmp(arg, "-A") || !strcmp(arg, "--show-all")) { + edarg++; + opts.show_all = 1; + opts.markers = 1; + continue; + } + if (!strcmp(arg, "-x") || !strcmp(arg, "--overlap-only")) { + edarg++; + opts.overlap_only = 1; + continue; + } + if (!strcmp(arg, "-X")) { + edarg++; + opts.overlap_only = 1; + opts.markers = 1; + continue; + } + if (!strcmp(arg, "-3") || !strcmp(arg, "--easy-only")) { + edarg++; + opts.unmerged_only = 1; + continue; + } + if (!strcmp(arg, "-m") || !strcmp(arg, "--merge")) { + opts.merge = 1; + continue; + } + if (!strcmp(arg, "-T") || !strcmp(arg, "--initial-tab")) { + opts.initial_tab = 1; + continue; + } + if (!strcmp(arg, "-L")) { + if ( argc <= i+1 ) + die("-L needs a LABEL argument"); + if ( label_count < 3 ) + label_strings[label_count++] = argv[++i]; + else + die("too many labels options specified"); + continue; + } + if (!strncmp(arg, "--label=", 8)) { + if ( !*(arg+8) ) + die("--label needs a LABEL argument"); + if ( label_count < 3 ) + label_strings[label_count++] = arg+8; + else + die("too many labels options specified"); + continue; + } + if (!strncmp(arg, "--diff-program=", 15)) { + if ( !*(arg+15) ) + die("--diff-program needs a PROGRAM argument"); + opts.diff_prog = arg+15; + continue; + } + if (*(arg+1) != '-') { + const char *a = arg+1; + + while (*a) { + switch (*a) { + case 'A': + edarg++; + opts.show_all = 1; + opts.markers = 1; + break; + case 'e': + edarg++; + break; + case 'E': + edarg++; + opts.markers = 1; + break; + case 'x': + edarg++; + opts.overlap_only = 1; + break; + case 'X': + edarg++; + opts.overlap_only = 1; + opts.markers = 1; + break; + case '3': + edarg++; + opts.unmerged_only = 1; + break; + case 'm': + opts.merge = 1; + break; + case 'T': + opts.initial_tab = 1; + break; + default: + error("unknown option: %c\n", *a); + usage(builtin_diff3_usage); + break; + } + a++; + } + continue; + } + error("unrecognised option: %s\n", arg); + usage(builtin_diff3_usage); + } + + if (edarg > 1) + die("at most one of the AeExX3 options are allowed"); + + /* -AeExX3 without -m implies ed script */ + opts.edscript = edarg && !opts.merge; + /* -m without -AeExX3 implies -A */ + if (opts.merge && !edarg) { + opts.show_all = 1; + opts.markers = 1; + } + + if (opts.edscript) + die("ed scripts not yet supported!"); + + if (argc != i+3) { + if ( argc < i+3 ) + error("missing file argument(s)\n"); + else + error("too many arguments ('%s'...)\n", argv[i+3]); + usage(builtin_diff3_usage); + } + + file = &argv[i]; + + for ( i = label_count; i < 3; i++ ) + label_strings[i] = file[i]; + + conflicts = diff3(stdout, file, label_strings, &opts); + + return conflicts > 0; +} + + diff --git a/builtin.h b/builtin.h index 26ebcaf..a0e9ab7 100644 --- a/builtin.h +++ b/builtin.h @@ -12,6 +12,8 @@ extern void help_unknown_cmd(const char extern int cmd_help(int argc, const char **argv, const char *prefix); extern int cmd_version(int argc, const char **argv, const char *prefix); +extern int cmd_diff3(int argc, const char **argv, const char *prefix); + extern int cmd_whatchanged(int argc, const char **argv, const char *prefix); extern int cmd_show(int argc, const char **argv, const char *prefix); extern int cmd_log(int argc, const char **argv, const char *prefix); diff --git a/git.c b/git.c index 18ba14a..a76c7b6 100644 --- a/git.c +++ b/git.c @@ -233,6 +233,7 @@ static void handle_internal_command(int { "format-patch", cmd_format_patch, NEEDS_PREFIX }, { "count-objects", cmd_count_objects }, { "diff", cmd_diff, NEEDS_PREFIX }, + { "diff3", cmd_diff3 }, { "grep", cmd_grep, NEEDS_PREFIX }, { "rm", cmd_rm, NEEDS_PREFIX }, { "add", cmd_add, NEEDS_PREFIX }, diff --git a/t/txxxx-diff3.sh b/t/txxxx-diff3.sh new file mode 100755 index 0000000..3b515e7 --- /dev/null +++ b/t/txxxx-diff3.sh @@ -0,0 +1,175 @@ +#!/bin/sh + +test_description='diff3 tests' + +. ./test-lib.sh + +########################################################### +# initialise counts + +diff3_count=0 +diff3_fails=0 +diff3_stats=0 +total_count=0 +total_fails=0 +total_stats=0 + +########################################################### +# test that "git diff3" and "diff3" give the same output +# and the same exit code for a given set of arguments. + +test_diff3 () { + + diff3_count=$(expr $diff3_count + 1) + + diff3 "$@" >out-diff3 + dret="$?" + + git diff3 "$@" >out-git + gret="$?" + + diff out-diff3 out-git >out-diff 2>&1 + + if [ "$?" != 0 ] + then + diff3_fails=$(expr $diff3_fails + 1) + echo "* FAIL: --- " "$@" " ---" + fi + if [ "$dret" != "$gret" ] + then + diff3_stats=$(expr $diff3_stats + 1) + echo "* STAT: ($dret != $gret) --- " "$@" " ---" + fi + +} + +########################################################### +# test that "git diff3" and "diff3" give the same output +# and the same exit code for several invocations of git +# with different external diff command lines. (ie "normal" +# diff and several unified diffs with context lines 0->5) + +test_diff3_extra () { + + diff3 "$@" >out-diff3 + dret="$?" + + for dopt in "" "-U0" "-U1" "-U2" "-u" "-U4" "-U5" + do + diff3_count=$(expr $diff3_count + 1) + + git diff3 --diff-program="diff $dopt" "$@" >out-git + gret="$?" + + diff out-diff3 out-git >out-diff 2>&1 + + if [ "$?" != 0 ] + then + diff3_fails=$(expr $diff3_fails + 1) + echo "* FAIL: --- --diff-program='diff $dopt'" "$@" " ---" + fi + if [ "$dret" != "$gret" ] + then + diff3_stats=$(expr $diff3_stats + 1) + echo "* STAT: ($dret != $gret) --- --diff-program='diff $dopt'" "$@" " ---" + fi + done + +} + +########################################################### +# given some options ($1) and three input files ($2->$4) +# test git diff3 on permutations of the input files. + +test_perm_files () { + test_diff3 $1 $2 $3 $4 + test_diff3 $1 $4 $3 $2 + test_diff3 $1 $3 $2 $4 + test_diff3 $1 $4 $2 $3 + test_diff3 $1 $2 $4 $3 + test_diff3 $1 $3 $4 $2 +} + +########################################################### +# given three input files ($1->$3) test git diff3 output +# ($opt == "") and all merge types for several permutaions +# of the input files. also test the external diff support. + +test_files () { + + diff3_count=0 + diff3_fails=0 + diff3_stats=0 + + for opt in "" "-mA" "-me" "-mE" "-mx" "-mX" "-m3" + do + test_perm_files $opt $1 $2 $3 + done + + test_diff3_extra -mA -L our $1 $2 $3 + test_diff3_extra -mA -L our -L base $1 $2 $3 + test_diff3_extra -mA -L our -L base -L their $1 $2 $3 + + total_count=$(expr $total_count + $diff3_count) + total_fails=$(expr $total_fails + $diff3_fails) + total_stats=$(expr $total_stats + $diff3_stats) + + fails_and_stats=$(expr $diff3_fails + $diff3_stats) + + case "$fails_and_stats" in + 0) + return 0 ;; + *) + echo " tests: $diff3_count, fails: $diff3_fails, stats: $diff3_stats" + return 1 ;; + esac + +} + +########################################################### +# ok so test some files. + +test_expect_success \ + "test files lao tzu tao" \ + 'test_files ../txxxx/lao ../txxxx/tzu ../txxxx/tao' + +test_expect_success \ + "test files lao1 tzu tao1" \ + 'test_files ../txxxx/lao1 ../txxxx/tzu ../txxxx/tao1' + +test_expect_success \ + "test files lao2 tzu tao2" \ + 'test_files ../txxxx/lao2 ../txxxx/tzu ../txxxx/tao2' + +test_expect_success \ + "test files lao3 tzu tao3" \ + 'test_files ../txxxx/lao3 ../txxxx/tzu ../txxxx/tao3' + +test_expect_success \ + "test files lao4 tzu tao4" \ + 'test_files ../txxxx/lao4 ../txxxx/tzu ../txxxx/tao4' + +test_expect_success \ + "test files tzu tzu tzu" \ + 'test_files ../txxxx/tzu ../txxxx/tzu ../txxxx/tzu' + +test_expect_success \ + "test files lao tzu tzu" \ + 'test_files ../txxxx/lao ../txxxx/tzu ../txxxx/tzu' + +test_expect_success \ + "test files lao tzu /dev/null" \ + 'test_files ../txxxx/lao ../txxxx/tzu /dev/null' + +test_expect_success \ + "test files /dev/null tzu /dev/null" \ + 'test_files /dev/null ../txxxx/tzu /dev/null' + +########################################################### +# ok we are done. + +echo " tests: $total_count, fails: $total_fails, stats: $total_stats" + +test_done + + diff --git a/t/txxxx/lao b/t/txxxx/lao new file mode 100644 index 0000000..635ef2c --- /dev/null +++ b/t/txxxx/lao @@ -0,0 +1,11 @@ +The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The Named is the mother of all things. +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. diff --git a/t/txxxx/lao1 b/t/txxxx/lao1 new file mode 100644 index 0000000..831654a --- /dev/null +++ b/t/txxxx/lao1 @@ -0,0 +1,8 @@ +The Nameless is the origin of Heaven and Earth; + + so we may see their subtlety, + so we may see their outcome. +The two are the same, +But after they are produced, +They both may be called deep and profound. +The door of all subtleties! diff --git a/t/txxxx/lao2 b/t/txxxx/lao2 new file mode 100644 index 0000000..a7babdc --- /dev/null +++ b/t/txxxx/lao2 @@ -0,0 +1,16 @@ +The Nameless is the origin of Heaven and Earth; +home, away +The named is the mother of all things. + +Amy, Helen, Kym +Therefore let there always be non-being, + so we may see their subtlety, +fred, bill, george +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! diff --git a/t/txxxx/lao3 b/t/txxxx/lao3 new file mode 100644 index 0000000..ea500e5 --- /dev/null +++ b/t/txxxx/lao3 @@ -0,0 +1,13 @@ +The Nameless is the origin of Heaven and Earth; +The named is the mother of ALL things. + +Therefore let there ALWAYS be non-being, + so we may see their subtlety, +And let there ALWAYS be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! diff --git a/t/txxxx/lao4 b/t/txxxx/lao4 new file mode 100644 index 0000000..2ca7ddd --- /dev/null +++ b/t/txxxx/lao4 @@ -0,0 +1,12 @@ +The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The Named is the mother of all things. +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +unfinished symphony \ No newline at end of file diff --git a/t/txxxx/tao b/t/txxxx/tao new file mode 100644 index 0000000..ffe1ba3 --- /dev/null +++ b/t/txxxx/tao @@ -0,0 +1,14 @@ +The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. + +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their result. +The two are the same, +But after they are produced, + they have different names. + + -- The Way of Lao-Tzu, tr. Wing-tsit Chan diff --git a/t/txxxx/tao1 b/t/txxxx/tao1 new file mode 100644 index 0000000..7c0e98a --- /dev/null +++ b/t/txxxx/tao1 @@ -0,0 +1,10 @@ +The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. +Therefore let there always be non-being, +And let there always be being, +The two are the same, +But after they are produced, + they have different names. +They both may be called DEEP and profound. +Deeper and more profound, +The door of ALL subtleties! diff --git a/t/txxxx/tao2 b/t/txxxx/tao2 new file mode 100644 index 0000000..033360b --- /dev/null +++ b/t/txxxx/tao2 @@ -0,0 +1,16 @@ +The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. +mars, venus, neptune + +Therefore let there always be non-being, +quartz, diamond, ruby + so we may see their subtlety, +And let there always be being, +lilly, rose, tulip + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! diff --git a/t/txxxx/tao3 b/t/txxxx/tao3 new file mode 100644 index 0000000..0457551 --- /dev/null +++ b/t/txxxx/tao3 @@ -0,0 +1,13 @@ +The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. +THEIR +Therefore let there always be non-being, + so we may see THEIR subtlety, +And let there always be being, + so we may see THEIR outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! diff --git a/t/txxxx/tao4 b/t/txxxx/tao4 new file mode 100644 index 0000000..fbe30fd --- /dev/null +++ b/t/txxxx/tao4 @@ -0,0 +1,15 @@ +The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. + +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their result. +The two are the same, +But after they are produced, + they have different names. + + -- The Way of Lao-Tzu, tr. Wing-tsit Chan +unchained melody \ No newline at end of file diff --git a/t/txxxx/tzu b/t/txxxx/tzu new file mode 100644 index 0000000..5af88a8 --- /dev/null +++ b/t/txxxx/tzu @@ -0,0 +1,13 @@ +The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. + +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! -- 1.4.2 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2006-11-21 22:24 (unknown) Johannes Schindelin 2006-11-21 23:15 ` [PATCH] xdiff: add xdl_merge() (was: (unknown)) Jakub Narebski 2006-11-22 19:58 ` [PATCH] xdiff: add xdl_merge() Ramsay Jones @ 2006-11-22 20:16 ` Davide Libenzi 2 siblings, 0 replies; 34+ messages in thread From: Davide Libenzi @ 2006-11-22 20:16 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git On Tue, 21 Nov 2006, Johannes Schindelin wrote: > [PATCH] xdiff: add xdl_merge() > > This new function implements the functionality of RCS merge, but > in-memory. It returns < 0 on error, otherwise the number of conflicts. > > Finding the conflicting lines can be a very expensive task. You can > control the eagerness of this algorithm: > > - a level value of 0 means that all overlapping changes are treated > as conflicts, > - a value of 1 means that if the overlapping changes are identical, > it is not treated as a conflict. > - If you set level to 2, overlapping changes will be analyzed, so that > almost identical changes will not result in huge conflicts. Rather, > only the conflicting lines will be shown inside conflict markers. > > With each increasing level, the algorithm gets slower, but more accurate. > Note that the code for level 2 depends on the simple definition of > mmfile_t specific to git, and therefore it will be harder to port that > to LibXDiff. Johannes, at the moment I'm chased by a huge storm of never ending emails, so I won't be able to follow up this one soon. A smart 3-way merge is in my plans for LibXDiff though. There is quite some nice code around, that does pretty smart tricks and goes down to resolve sub-hunk non-trivial conflicts. You may want to take a look at that code too. - Davide ^ permalink raw reply [flat|nested] 34+ messages in thread
* (no subject) @ 2020-06-24 0:38 shejan shuza 2020-06-24 1:31 ` your mail brian m. carlson 0 siblings, 1 reply; 34+ messages in thread From: shejan shuza @ 2020-06-24 0:38 UTC (permalink / raw) To: git Hi, I have a repository with about 55GB of contents, with binary files that are less than 100MB each (so no LFS mode) from a project which has almost filled up an entire hard drive. I am trying to add all of the contents to a git repo and push it to GitHub but every time I do git add . in the folder with my contents after initializing and setting my remote, git starts caching all the files to .git/objects, making the .git folder grow in size rapidly. All the files are binaries, so git cannot stage changes between versions anyway, so there is no reason to cache versions. Is there any way, such as editing the git attributes or changing something about how files are staged in the git repository, to only just add indexes or references to files in the repository rather than cache them into the .git folder, while also being able to push all the data to GitHub? ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2020-06-24 0:38 shejan shuza @ 2020-06-24 1:31 ` brian m. carlson 0 siblings, 0 replies; 34+ messages in thread From: brian m. carlson @ 2020-06-24 1:31 UTC (permalink / raw) To: shejan shuza; +Cc: git [-- Attachment #1: Type: text/plain, Size: 2847 bytes --] On 2020-06-24 at 00:38:39, shejan shuza wrote: > Hi, I have a repository with about 55GB of contents, with binary files > that are less than 100MB each (so no LFS mode) from a project which > has almost filled up an entire hard drive. I am trying to add all of > the contents to a git repo and push it to GitHub but every time I do > > git add . > > in the folder with my contents after initializing and setting my > remote, git starts caching all the files to .git/objects, making the > .git folder grow in size rapidly. All the files are binaries, so git > cannot stage changes between versions anyway, so there is no reason to > cache versions. What you're experiencing is normal; storing files in the .git directory is how Git keeps track of them. It can't rely on the copies in your working tree because you can modify those files at any time, and if you did so, relying on them would corrupt the repository. Also, note that Git can and does deltify changes between revisions once the data is packed, regardless of whether the file is binary, but how well it does so depends on your data. For example, if it's compressed, it likely doesn't deltify very well, so storing things like compressed images or zip files using deflate is generally going to result in a bloated repository. However, if you don't need versioning for these files, then you don't need a Git repository. Git is a tool for versioning files, not a general storage mechanism. You may find a cloud storage bucket or some other artifact storage service may meet your needs better. I will also tell you from a practical point of view that almost nobody (including you) will want to host a 55 GB repository filled with binary blobs. Usually repacking these repositories is very expensive, requiring extensive CPU and memory usage for a prolonged time for little useful benefit. > Is there any way, such as editing the git attributes or changing > something about how files are staged in the git repository, to only > just add indexes or references to files in the repository rather than > cache them into the .git folder, while also being able to push all the > data to GitHub? This is how Git LFS and similar tools, like git-annex, work. Git LFS will create copies of the objects in your .git directory though, at least until they're pushed to the server, at which point they can be pruned. Git LFS has the same limitation as Git here. I'm less familiar with git-annex, but it is also a popular choice. However, as mentioned, it sounds like you don't need versioning at all, so unless you do, Git with Git LFS will be no more suitable for this than plain Git. If that's the case, I encourage you to explore alternate solutions. -- brian m. carlson: Houston, Texas, US OpenPGP: https://keybase.io/bk2204 [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 263 bytes --] ^ permalink raw reply [flat|nested] 34+ messages in thread
* (no subject) @ 2019-11-20 3:49 Han-Wen Nienhuys 2019-11-20 5:30 ` your mail Taylor Blau 0 siblings, 1 reply; 34+ messages in thread From: Han-Wen Nienhuys @ 2019-11-20 3:49 UTC (permalink / raw) To: git, Christian Couder, Johannes Schindelin Hey folks, I spent the last few weeks cobbling together an implementation of the reftable format in C and in Go. I thought this would be cool to add to git-core, but I doubt whether I will have enough time to see such an effort through. Maybe some of you would want to try integrating it into the Git-core code base? Example code is here: https://github.com/google/reftable/blob/master/c/api.h#L153 cheers! -- Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2019-11-20 3:49 Han-Wen Nienhuys @ 2019-11-20 5:30 ` Taylor Blau 2019-11-20 8:05 ` Christian Couder 0 siblings, 1 reply; 34+ messages in thread From: Taylor Blau @ 2019-11-20 5:30 UTC (permalink / raw) To: Han-Wen Nienhuys; +Cc: git, Christian Couder, Johannes Schindelin Hi Han-Wen, On Tue, Nov 19, 2019 at 07:49:17PM -0800, Han-Wen Nienhuys wrote: > Hey folks, > > I spent the last few weeks cobbling together an implementation of the > reftable format in C and in Go. I thought this would be cool to add to > git-core, but I doubt whether I will have enough time to see such an > effort through. Maybe some of you would want to try integrating it > into the Git-core code base? Example code is here: > > https://github.com/google/reftable/blob/master/c/api.h#L153 Very exciting, and thanks for your work on this. I haven't taken a close look at the code yet, so I'm sure taking this further involves a much more careful examination. I know that Christian Couder (who you CC-d on this thread) was also working on this for either GitLab or Booking.com. Christian -- are you still working on this for either one of those entities? If so, is there some way to unify these two efforts? > cheers! > -- Thanks, Taylor ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2019-11-20 5:30 ` your mail Taylor Blau @ 2019-11-20 8:05 ` Christian Couder 0 siblings, 0 replies; 34+ messages in thread From: Christian Couder @ 2019-11-20 8:05 UTC (permalink / raw) To: Taylor Blau; +Cc: Han-Wen Nienhuys, git, Johannes Schindelin Hi Taylor and Han-Wen, On Wed, Nov 20, 2019 at 6:30 AM Taylor Blau <me@ttaylorr.com> wrote: > > On Tue, Nov 19, 2019 at 07:49:17PM -0800, Han-Wen Nienhuys wrote: > > > > I spent the last few weeks cobbling together an implementation of the > > reftable format in C and in Go. I thought this would be cool to add to > > git-core, but I doubt whether I will have enough time to see such an > > effort through. Maybe some of you would want to try integrating it > > into the Git-core code base? Example code is here: > > > > https://github.com/google/reftable/blob/master/c/api.h#L153 Interesting! > Very exciting, and thanks for your work on this. I haven't taken a > close look at the code yet, so I'm sure taking this further involves a > much more careful examination. > > I know that Christian Couder (who you CC-d on this thread) was also > working on this for either GitLab or Booking.com. > > Christian -- are you still working on this for either one of those > entities? If so, is there some way to unify these two efforts? Yeah, I started working on this last year for Booking.com, and I am now slowly working on it for GitLab. It is not yet finished because it has been low priority work. I took a very quick look at Han-Wen's implementation and it looks very different from mine from the outside. It seems more complete, but might be less integrated into the Git code base. Best, Christian. ^ permalink raw reply [flat|nested] 34+ messages in thread
* (no subject) @ 2019-07-11 20:11 Robert Morgan 2019-07-11 20:18 ` your mail Kevin Daudt 0 siblings, 1 reply; 34+ messages in thread From: Robert Morgan @ 2019-07-11 20:11 UTC (permalink / raw) To: git subscribe git ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2019-07-11 20:11 Robert Morgan @ 2019-07-11 20:18 ` Kevin Daudt 2019-07-11 20:25 ` Robert Morgan 0 siblings, 1 reply; 34+ messages in thread From: Kevin Daudt @ 2019-07-11 20:18 UTC (permalink / raw) To: Robert Morgan; +Cc: git On Thu, Jul 11, 2019 at 03:11:33PM -0500, Robert Morgan wrote: > subscribe git You need to send this to majordomo@vger.kernel.org. Sending it to the git mailing list will not do a lot. Kevin ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2019-07-11 20:18 ` your mail Kevin Daudt @ 2019-07-11 20:25 ` Robert Morgan 0 siblings, 0 replies; 34+ messages in thread From: Robert Morgan @ 2019-07-11 20:25 UTC (permalink / raw) To: Kevin Daudt, Robert Morgan, git Apologies list! Thanks Kevin. That's what I get for troubleshooting plain-text in Gmail and quickly sending a subscribe email before walking out. Robert On Thu, Jul 11, 2019 at 3:18 PM Kevin Daudt <me@ikke.info> wrote: > > On Thu, Jul 11, 2019 at 03:11:33PM -0500, Robert Morgan wrote: > > subscribe git > > You need to send this to majordomo@vger.kernel.org. Sending it to the > git mailing list will not do a lot. > > Kevin ^ permalink raw reply [flat|nested] 34+ messages in thread
* (no subject) @ 2019-01-25 9:47 Furkan DURUL 2019-01-25 11:27 ` your mail Kevin Daudt 0 siblings, 1 reply; 34+ messages in thread From: Furkan DURUL @ 2019-01-25 9:47 UTC (permalink / raw) To: git Hello, My name is Furkan. I'm a new graduated Electronics and Communication Engineer in Turkey. In my current workplace, we use Git in all our projects and I really learned a lot from your Pro Git Book. I would like to contribute to translation of this book to Turkish. I'm also a freelance translator and have experience about translation. Look forward to hear you Cheers ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2019-01-25 9:47 Furkan DURUL @ 2019-01-25 11:27 ` Kevin Daudt 0 siblings, 0 replies; 34+ messages in thread From: Kevin Daudt @ 2019-01-25 11:27 UTC (permalink / raw) To: Furkan DURUL; +Cc: git On Fri, Jan 25, 2019 at 12:47:35PM +0300, Furkan DURUL wrote: > Hello, > My name is Furkan. I'm a new graduated Electronics and Communication > Engineer in Turkey. In my current workplace, we use Git in all our > projects and I really learned a lot from your Pro Git Book. I would > like to contribute to translation of this book to Turkish. I'm also a > freelance translator and have experience about translation. > Look forward to hear you > Cheers Hello Furkan, Welcome to the community. Pro Git is not maintained by the git community, but is a separate project. You can find details on how to provide translations here[0]. It looks from that document that someone started translating the book in Turkish as well[1]. Kind regards, Kevin [0]:https://github.com/progit/progit2/blob/master/TRANSLATING.md [1]:https://github.com/progit/progit2-tr ^ permalink raw reply [flat|nested] 34+ messages in thread
* (no subject) @ 2017-06-22 9:50 Jessie Hernandez 2017-06-22 12:48 ` your mail Simon Ruderich 0 siblings, 1 reply; 34+ messages in thread From: Jessie Hernandez @ 2017-06-22 9:50 UTC (permalink / raw) To: git subscribe git ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 9:50 Jessie Hernandez @ 2017-06-22 12:48 ` Simon Ruderich 2017-06-22 13:35 ` AW: " Patrick Lehmann 0 siblings, 1 reply; 34+ messages in thread From: Simon Ruderich @ 2017-06-22 12:48 UTC (permalink / raw) To: Jessie Hernandez; +Cc: git On Thu, Jun 22, 2017 at 11:50:01AM +0200, Jessie Hernandez wrote: > subscribe git You need to write to majordomo@vger.kernel.org (with subscribe git in the body) to subscribe. Regards Simon -- + privacy is necessary + using gnupg http://gnupg.org + public key id: 0x92FEFDB7E44C32F9 ^ permalink raw reply [flat|nested] 34+ messages in thread
* AW: your mail 2017-06-22 12:48 ` your mail Simon Ruderich @ 2017-06-22 13:35 ` Patrick Lehmann 2017-06-22 13:47 ` Simon Ruderich 0 siblings, 1 reply; 34+ messages in thread From: Patrick Lehmann @ 2017-06-22 13:35 UTC (permalink / raw) To: Simon Ruderich, Jessie Hernandez; +Cc: git@vger.kernel.org But how can he write to the mailing list without a subscription? Is this a security bug or is he already subscribed? ________________________________________ Von: git-owner@vger.kernel.org [git-owner@vger.kernel.org]" im Auftrag von "Simon Ruderich [simon@ruderich.org] Gesendet: Donnerstag, 22. Juni 2017 14:48 Bis: Jessie Hernandez Cc: git@vger.kernel.org Betreff: Re: your mail On Thu, Jun 22, 2017 at 11:50:01AM +0200, Jessie Hernandez wrote: > subscribe git You need to write to majordomo@vger.kernel.org (with subscribe git in the body) to subscribe. Regards Simon -- + privacy is necessary + using gnupg http://gnupg.org + public key id: 0x92FEFDB7E44C32F9 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 13:35 ` AW: " Patrick Lehmann @ 2017-06-22 13:47 ` Simon Ruderich 2017-06-22 13:55 ` AW: " Patrick Lehmann 0 siblings, 1 reply; 34+ messages in thread From: Simon Ruderich @ 2017-06-22 13:47 UTC (permalink / raw) To: Patrick Lehmann; +Cc: Jessie Hernandez, git On Thu, Jun 22, 2017 at 01:35:33PM +0000, Patrick Lehmann wrote: > But how can he write to the mailing list without a subscription? > Is this a security bug or is he already subscribed? Everybody can send to this mailing list. This is by design so contributors/bug reporters can send mails without having to subscribe. Regards Simon -- + Privatsphäre ist notwendig + Ich verwende GnuPG http://gnupg.org + Öffentlicher Schlüssel: 0x92FEFDB7E44C32F9 ^ permalink raw reply [flat|nested] 34+ messages in thread
* AW: your mail 2017-06-22 13:47 ` Simon Ruderich @ 2017-06-22 13:55 ` Patrick Lehmann 2017-06-22 20:46 ` Simon Ruderich 0 siblings, 1 reply; 34+ messages in thread From: Patrick Lehmann @ 2017-06-22 13:55 UTC (permalink / raw) To: Simon Ruderich; +Cc: Jessie Hernandez, git@vger.kernel.org The description on https://github.com/git/git doesn't reflect that policy. a) It explains that discussions take place in the mentioned mailing list. b) It describes how to subscribe. With knowledge of other mailing lists (mostly managed by mailman), subscription is required for participation. ________________________________________ Von: Simon Ruderich [simon@ruderich.org] Gesendet: Donnerstag, 22. Juni 2017 15:47 Bis: Patrick Lehmann Cc: Jessie Hernandez; git@vger.kernel.org Betreff: Re: your mail On Thu, Jun 22, 2017 at 01:35:33PM +0000, Patrick Lehmann wrote: > But how can he write to the mailing list without a subscription? > Is this a security bug or is he already subscribed? Everybody can send to this mailing list. This is by design so contributors/bug reporters can send mails without having to subscribe. Regards Simon -- + Privatsphäre ist notwendig + Ich verwende GnuPG http://gnupg.org + Öffentlicher Schlüssel: 0x92FEFDB7E44C32F9 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 13:55 ` AW: " Patrick Lehmann @ 2017-06-22 20:46 ` Simon Ruderich 2017-06-22 21:35 ` Junio C Hamano 0 siblings, 1 reply; 34+ messages in thread From: Simon Ruderich @ 2017-06-22 20:46 UTC (permalink / raw) To: Patrick Lehmann; +Cc: Jessie Hernandez, git@vger.kernel.org On Thu, Jun 22, 2017 at 01:55:27PM +0000, Patrick Lehmann wrote: > The description on https://github.com/git/git doesn't reflect that policy. > > a) > It explains that discussions take place in the mentioned mailing list. > b) > It describes how to subscribe. However it doesn't say that you have to subscribe to send, only how to subscribe. > With knowledge of other mailing lists (mostly managed by mailman), > subscription is required for participation. That depends on the mailing list, some require subscription to prevent spams but not all do. Somebody might want to update the documentation, but personally I see no reason to change anything. If you want to send a patch to improve it, that would be great of course. Regards Simon PS: Please don't top-post on this mailing list. -- + Privatsphäre ist notwendig + Ich verwende GnuPG http://gnupg.org + Öffentlicher Schlüssel: 0x92FEFDB7E44C32F9 ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 20:46 ` Simon Ruderich @ 2017-06-22 21:35 ` Junio C Hamano 2017-06-22 21:58 ` Ævar Arnfjörð Bjarmason 0 siblings, 1 reply; 34+ messages in thread From: Junio C Hamano @ 2017-06-22 21:35 UTC (permalink / raw) To: Simon Ruderich; +Cc: Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org Simon Ruderich <simon@ruderich.org> writes: > On Thu, Jun 22, 2017 at 01:55:27PM +0000, Patrick Lehmann wrote: >> The description on https://github.com/git/git doesn't reflect that policy. >> >> a) >> It explains that discussions take place in the mentioned mailing list. >> b) >> It describes how to subscribe. > > However it doesn't say that you have to subscribe to send, only > how to subscribe. For that matter, we also say "everyone is welcome to post", which makes it clear that no subscription is required. But I view these merely being technically correct. And making it absolutely obvious does not cost too much. >> With knowledge of other mailing lists (mostly managed by mailman), >> subscription is required for participation. > > That depends on the mailing list, some require subscription to > prevent spams but not all do. Yes. But not many people realize that the world they know is the only world. We are used to an open list and are shocked when we encouter a closed one; let's not forget that shock. How about doing it like this? README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f17af66a97..bbaf54bffb 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ The user discussion and development of Git take place on the Git mailing list -- everyone is welcome to post bug reports, feature requests, comments and patches to git@vger.kernel.org (read [Documentation/SubmittingPatches][] for instructions on patch submission). + +You can send messages without subscribing to the list, but it is +recommended to read what other people are saying on the list before +you speak. To subscribe to the list, send an email with just "subscribe git" in the body to majordomo@vger.kernel.org. The mailing list archives are available at <https://public-inbox.org/git/>, ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 21:35 ` Junio C Hamano @ 2017-06-22 21:58 ` Ævar Arnfjörð Bjarmason 2017-06-22 22:14 ` Junio C Hamano ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Ævar Arnfjörð Bjarmason @ 2017-06-22 21:58 UTC (permalink / raw) To: Junio C Hamano Cc: Simon Ruderich, Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org On Thu, Jun 22 2017, Junio C. Hamano jotted: > Simon Ruderich <simon@ruderich.org> writes: > >> On Thu, Jun 22, 2017 at 01:55:27PM +0000, Patrick Lehmann wrote: >>> The description on https://github.com/git/git doesn't reflect that policy. >>> >>> a) >>> It explains that discussions take place in the mentioned mailing list. >>> b) >>> It describes how to subscribe. >> >> However it doesn't say that you have to subscribe to send, only >> how to subscribe. > > For that matter, we also say "everyone is welcome to post", which > makes it clear that no subscription is required. > > But I view these merely being technically correct. And making it > absolutely obvious does not cost too much. > >>> With knowledge of other mailing lists (mostly managed by mailman), >>> subscription is required for participation. >> >> That depends on the mailing list, some require subscription to >> prevent spams but not all do. > > Yes. But not many people realize that the world they know is the > only world. We are used to an open list and are shocked when we > encouter a closed one; let's not forget that shock. > > How about doing it like this? > > README.md | 4 ++++ > 1 file changed, 4 insertions(+) > > diff --git a/README.md b/README.md > index f17af66a97..bbaf54bffb 100644 > --- a/README.md > +++ b/README.md > @@ -31,6 +31,10 @@ The user discussion and development of Git take place on the Git > mailing list -- everyone is welcome to post bug reports, feature > requests, comments and patches to git@vger.kernel.org (read > [Documentation/SubmittingPatches][] for instructions on patch submission). > + > +You can send messages without subscribing to the list, but it is > +recommended to read what other people are saying on the list before > +you speak. > To subscribe to the list, send an email with just "subscribe git" in > the body to majordomo@vger.kernel.org. The mailing list archives are > available at <https://public-inbox.org/git/>, It's unclear what that means. I *think* it means "consider taking a look around the list before you post", but then it's probably better advice to tell people to skim the archives first to get an idea of the traffic. E.g. if I page through the first 2 pages of public-inbox.org I get messages going back to the 19th, but if I were to subscribe to the list I'd need to wait 4 days to get the same mail. Which, in the context of what this follows (how to submit a bug, questions etc.) isn't a good use of time for the person reading the instructions. Maybe something more like: diff --git a/README.md b/README.md index f17af66a97..dc175757fa 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,12 @@ the body to majordomo@vger.kernel.org. The mailing list archives are available at <https://public-inbox.org/git/>, <http://marc.info/?l=git> and other archival sites. +You don't need to be subscribed to the list to send mail to it, and +others on-list will generally CC you when replying (although some +forget this). It's adviced to subscribe to the list if you want to be +sure you're not missing follow-up discussion, or if your interest in +the project is wider than a one-off bug report, question or patch. + The maintainer frequently sends the "What's cooking" reports that list the current status of various development topics to the mailing list. The discussion following them give a good reference for ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 21:58 ` Ævar Arnfjörð Bjarmason @ 2017-06-22 22:14 ` Junio C Hamano 2017-06-22 23:21 ` Jeff King 2017-06-23 6:58 ` demerphq 2 siblings, 0 replies; 34+ messages in thread From: Junio C Hamano @ 2017-06-22 22:14 UTC (permalink / raw) To: Ævar Arnfjörð Bjarmason Cc: Simon Ruderich, Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes: > Maybe something more like: Much better. > diff --git a/README.md b/README.md > index f17af66a97..dc175757fa 100644 > --- a/README.md > +++ b/README.md > @@ -36,6 +36,12 @@ the body to majordomo@vger.kernel.org. The mailing list archives are > available at <https://public-inbox.org/git/>, > <http://marc.info/?l=git> and other archival sites. > > +You don't need to be subscribed to the list to send mail to it, and > +others on-list will generally CC you when replying (although some > +forget this). It's adviced to subscribe to the list if you want to be > +sure you're not missing follow-up discussion, or if your interest in > +the project is wider than a one-off bug report, question or patch. > + > The maintainer frequently sends the "What's cooking" reports that > list the current status of various development topics to the mailing > list. The discussion following them give a good reference for ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 21:58 ` Ævar Arnfjörð Bjarmason 2017-06-22 22:14 ` Junio C Hamano @ 2017-06-22 23:21 ` Jeff King 2017-06-23 5:23 ` Junio C Hamano 2017-06-23 6:58 ` demerphq 2 siblings, 1 reply; 34+ messages in thread From: Jeff King @ 2017-06-22 23:21 UTC (permalink / raw) To: Ævar Arnfjörð Bjarmason Cc: Junio C Hamano, Simon Ruderich, Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org On Thu, Jun 22, 2017 at 11:58:08PM +0200, Ævar Arnfjörð Bjarmason wrote: > Which, in the context of what this follows (how to submit a bug, > questions etc.) isn't a good use of time for the person reading the > instructions. > > Maybe something more like: > > diff --git a/README.md b/README.md > index f17af66a97..dc175757fa 100644 > --- a/README.md > +++ b/README.md > @@ -36,6 +36,12 @@ the body to majordomo@vger.kernel.org. The mailing list archives are > available at <https://public-inbox.org/git/>, > <http://marc.info/?l=git> and other archival sites. > > +You don't need to be subscribed to the list to send mail to it, and > +others on-list will generally CC you when replying (although some > +forget this). It's adviced to subscribe to the list if you want to be > +sure you're not missing follow-up discussion, or if your interest in > +the project is wider than a one-off bug report, question or patch. > + > The maintainer frequently sends the "What's cooking" reports that > list the current status of various development topics to the mailing > list. The discussion following them give a good reference for You perhaps already read it, but you may want to steal wording or suggestions from the mailing list section at: https://git-scm.com/community which is covering the same ideas (and vice versa, patches to that page are welcome if the README says something better). -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 23:21 ` Jeff King @ 2017-06-23 5:23 ` Junio C Hamano 2017-06-23 16:53 ` Jeff King 0 siblings, 1 reply; 34+ messages in thread From: Junio C Hamano @ 2017-06-23 5:23 UTC (permalink / raw) To: Jeff King Cc: Ævar Arnfjörð Bjarmason, Simon Ruderich, Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org Jeff King <peff@peff.net> writes: >> +You don't need to be subscribed to the list to send mail to it, and >> +others on-list will generally CC you when replying (although some >> +forget this). It's adviced to subscribe to the list if you want to be >> +sure you're not missing follow-up discussion, or if your interest in >> +the project is wider than a one-off bug report, question or patch. >> + >> The maintainer frequently sends the "What's cooking" reports that >> list the current status of various development topics to the mailing >> list. The discussion following them give a good reference for > > You perhaps already read it, but you may want to steal wording or > suggestions from the mailing list section at: > > https://git-scm.com/community > > which is covering the same ideas (and vice versa, patches to that page > are welcome if the README says something better). OK, so... Ævar's version does not mention: - text/plain, which is a very good addition. - note about CC in a way as useful as the "community" page does, which may want to steal from the latter. - the archive, but it may not be needed in the context of this document. "Read the archive to make sure you are not repeating somebody else said before speaking" is something we silently wish everybody to follow, but it is something we do not want to say out loud, especially to new people. - Windows, but I am not sure if it is necessary or even healthy. One thing I would rather not to see is the Windows mailing list becomes the first line triage place for any and all issues that were experienced by a user who happened to be using Windows, and majority of the issues turn out to be unspecific to Windows. I'd suspect that all of us rather would want to see the referral go the other way around. Otoh, "community" page does not encourage subscription as a way to ensure you'll see follow-up discussion, which may be a good thing to add. A tangent I just found funny is this paragraph on the "community" page: The archive can be found on public-inbox. Click here to subscribe. Of course clicking does not take you to a subscription page for public-inbox, even though the two sentences appear to be related. Perhaps swap the order of the two, like so, with a bit richer explanation taken from Ævar's version: ... disable HTML in your outgoing messages. By subscribing (click here), you can make sure you're not missing follow-up discussion and also learn from other development in the community. The list archive can be found on public-inbox. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-23 5:23 ` Junio C Hamano @ 2017-06-23 16:53 ` Jeff King 2017-06-23 18:44 ` Junio C Hamano 0 siblings, 1 reply; 34+ messages in thread From: Jeff King @ 2017-06-23 16:53 UTC (permalink / raw) To: Junio C Hamano Cc: Ævar Arnfjörð Bjarmason, Simon Ruderich, Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org On Thu, Jun 22, 2017 at 10:23:17PM -0700, Junio C Hamano wrote: > Otoh, "community" page does not encourage subscription as a way to > ensure you'll see follow-up discussion, which may be a good thing to > add. > > A tangent I just found funny is this paragraph on the "community" > page: > > The archive can be found on public-inbox. Click here to > subscribe. > > Of course clicking does not take you to a subscription page for > public-inbox, even though the two sentences appear to be related. > > Perhaps swap the order of the two, like so, with a bit richer > explanation taken from Ævar's version: > > ... disable HTML in your outgoing messages. > > By subscribing (click here), you can make sure you're not > missing follow-up discussion and also learn from other > development in the community. The list archive can be found > on public-inbox. Yeah, I think that's a good suggestion. Do you want to phrase it in the form of a patch? :) -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-23 16:53 ` Jeff King @ 2017-06-23 18:44 ` Junio C Hamano 0 siblings, 0 replies; 34+ messages in thread From: Junio C Hamano @ 2017-06-23 18:44 UTC (permalink / raw) To: Jeff King Cc: Ævar Arnfjörð Bjarmason, Simon Ruderich, Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org Jeff King <peff@peff.net> writes: > On Thu, Jun 22, 2017 at 10:23:17PM -0700, Junio C Hamano wrote: > >> Otoh, "community" page does not encourage subscription as a way to >> ensure you'll see follow-up discussion, which may be a good thing to >> add. >> >> A tangent I just found funny is this paragraph on the "community" >> page: >> >> The archive can be found on public-inbox. Click here to >> subscribe. >> >> Of course clicking does not take you to a subscription page for >> public-inbox, even though the two sentences appear to be related. >> >> Perhaps swap the order of the two, like so, with a bit richer >> explanation taken from Ævar's version: >> >> ... disable HTML in your outgoing messages. >> >> By subscribing (click here), you can make sure you're not >> missing follow-up discussion and also learn from other >> development in the community. The list archive can be found >> on public-inbox. > > Yeah, I think that's a good suggestion. Do you want to phrase it in the > form of a patch? :) OK. Letme try. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2017-06-22 21:58 ` Ævar Arnfjörð Bjarmason 2017-06-22 22:14 ` Junio C Hamano 2017-06-22 23:21 ` Jeff King @ 2017-06-23 6:58 ` demerphq 2 siblings, 0 replies; 34+ messages in thread From: demerphq @ 2017-06-23 6:58 UTC (permalink / raw) To: Ævar Arnfjörð Bjarmason Cc: Junio C Hamano, Simon Ruderich, Patrick Lehmann, Jessie Hernandez, git@vger.kernel.org On 22 June 2017 at 23:58, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote: > +You don't need to be subscribed to the list to send mail to it, and > +others on-list will generally CC you when replying (although some > +forget this). It's adviced to subscribe to the list if you want to be FWIW: "adviced" is misspelled, it should be "advised", and IMO, it feels like poor style to begin a sentence with a contraction. Not strictly wrong, but sufficiently informal that I think it is out of place in docs like this. Better to just say "It is", or even just "You are", especially as you use "you" later in the sentence. I actually think simplifying that sentence considerably is preferable: "To be sure you receive all follow-up mails you should subscribe to the list." flows better and is more succinct than "It's advised to subscribe to the list if you want to be sure you're not missing follow-up discussion". > +sure you're not missing follow-up discussion, or if your interest in > +the project is wider than a one-off bug report, question or patch. cheers, yves -- perl -Mre=debug -e "/just|another|perl|hacker/" ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown), @ 2016-04-11 19:04 miwilliams 2016-04-11 19:13 ` your mail Jeff King 0 siblings, 1 reply; 34+ messages in thread From: miwilliams @ 2016-04-11 19:04 UTC (permalink / raw) To: git From 7201fe08ede76e502211a781250c9a0b702a78b2 Mon Sep 17 00:00:00 2001 From: Mike Williams <miwilliams@google.com> Date: Mon, 11 Apr 2016 14:18:39 -0400 Subject: [PATCH 1/1] wt-status: Remove '!!' from wt_status_collect_changed_cb The wt_status_collect_changed_cb function uses an extraneous double negation (!!) when determining whether or not a submodule has new commits. Signed-off-by: Mike Williams <miwilliams@google.com> --- wt-status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wt-status.c b/wt-status.c index ef74864..b955179 100644 --- a/wt-status.c +++ b/wt-status.c @@ -431,7 +431,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, d->worktree_status = p->status; d->dirty_submodule = p->two->dirty_submodule; if (S_ISGITLINK(p->two->mode)) - d->new_submodule_commits = !!hashcmp(p->one->sha1, p->two->sha1); + d->new_submodule_commits = hashcmp(p->one->sha1, p->two->sha1); } } -- 2.8.0 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2016-04-11 19:04 (unknown), miwilliams @ 2016-04-11 19:13 ` Jeff King 0 siblings, 0 replies; 34+ messages in thread From: Jeff King @ 2016-04-11 19:13 UTC (permalink / raw) To: miwilliams; +Cc: git On Mon, Apr 11, 2016 at 07:04:23PM +0000, miwilliams@google.com wrote: > From 7201fe08ede76e502211a781250c9a0b702a78b2 Mon Sep 17 00:00:00 2001 > From: Mike Williams <miwilliams@google.com> > Date: Mon, 11 Apr 2016 14:18:39 -0400 > Subject: [PATCH 1/1] wt-status: Remove '!!' from > wt_status_collect_changed_cb These bits (minus the initial "From ..." line) should go into your actual email headers. As it is, your email has no subject line. > The wt_status_collect_changed_cb function uses an extraneous double > negation (!!) when determining whether or not a submodule has new > commits. > [...] > - d->new_submodule_commits = !!hashcmp(p->one->sha1, p->two->sha1); > + d->new_submodule_commits = hashcmp(p->one->sha1, p->two->sha1); It's not extraneous. hashcmp() returns 0 for equality, but an arbitrary positive or negative value depending on how the two arguments differ. The assigned "new_submodule_commits" is a bitfield of size 1. So the "!!" is normalizing the value into "0" or "1". -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown), @ 2013-05-17 18:02 ASHISH VERMA 2013-05-21 13:13 ` your mail Magnus Bäck 0 siblings, 1 reply; 34+ messages in thread From: ASHISH VERMA @ 2013-05-17 18:02 UTC (permalink / raw) To: git Hi, i am using git to push my code from eclipse to heroku , but recently iam getting error like:: master:master rejected:non fast forward and i am not able to resolve it .I tried a git pull but that says - conflicts shud be resolved before git pull can be implemented.I can;t find the solution Please help ASAP... ASHISH ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2013-05-17 18:02 (unknown), ASHISH VERMA @ 2013-05-21 13:13 ` Magnus Bäck 0 siblings, 0 replies; 34+ messages in thread From: Magnus Bäck @ 2013-05-21 13:13 UTC (permalink / raw) To: ASHISH VERMA; +Cc: git On Friday, May 17, 2013 at 14:02 EDT, ASHISH VERMA <ashunew1989@gmail.com> wrote: > Hi, i am using git to push my code from eclipse to heroku , but > recently iam getting error like:: > > master:master rejected:non fast forward > > and i am not able to resolve it .I tried a git pull but that says - > conflicts shud be resolved before git pull can be implemented.I can;t > find the solution Please help ASAP... Resolve the conflicts by either hand-editing the files (you'll see the markers where the conflicts are) add running 'git add' to tell git that you're done resolving them, or set up Git to use a graphical tool like kdiff3. When you're done, run 'git commit' to create the merge commit. After that you'll be able to push to your upstream (unless things have moved again while you were resolving the initial conflict). This guide to merge conflicts looks pretty good: http://gitguru.com/2009/02/22/integrating-git-with-a-visual-merge-tool/ -- Magnus Bäck baeck@google.com ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown), @ 2012-05-06 14:17 Bruce Zu 2012-05-06 17:04 ` your mail Marcus Karlsson 0 siblings, 1 reply; 34+ messages in thread From: Bruce Zu @ 2012-05-06 14:17 UTC (permalink / raw) To: git I want subscribe this git mail list thanks! Bruce Zu ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2012-05-06 14:17 (unknown), Bruce Zu @ 2012-05-06 17:04 ` Marcus Karlsson 0 siblings, 0 replies; 34+ messages in thread From: Marcus Karlsson @ 2012-05-06 17:04 UTC (permalink / raw) To: Bruce Zu; +Cc: git On Sun, May 06, 2012 at 10:17:07PM +0800, Bruce Zu wrote: > I want subscribe this git mail list > thanks! > Bruce Zu Great idea. The instructions for how to subscribe can be found on http://vger.kernel.org/. Then subscribe to the list named 'git'. Marcus ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown), @ 2008-08-13 14:54 aneesh.kumar 2008-08-13 15:16 ` your mail Aneesh Kumar K.V 0 siblings, 1 reply; 34+ messages in thread From: aneesh.kumar @ 2008-08-13 14:54 UTC (permalink / raw) To: pasky; +Cc: git, Aneesh Kumar K.V From: Aneesh Kumar K.V <aneesh.kumar@gmail.com> topgit: Implement tg-import This can be used to import a set of commits between range specified by range1..range2 This should help us to convert an already existing quilt, stgit branches to topgit managed one Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com> --- Makefile | 2 +- README | 7 ++++++ tg-create.sh | 22 ++++++++---------- tg-export.sh | 2 +- tg-import.sh | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 6eade1e..95624ac 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ sharedir = $(PREFIX)/share/topgit hooksdir = $(cmddir)/hooks -commands_in = tg-create.sh tg-delete.sh tg-export.sh tg-info.sh tg-patch.sh tg-summary.sh tg-update.sh +commands_in = tg-create.sh tg-delete.sh tg-export.sh tg-info.sh tg-patch.sh tg-summary.sh tg-update.sh tg-import.sh hooks_in = hooks/pre-commit.sh commands_out = $(patsubst %.sh,%,$(commands_in)) diff --git a/README b/README index b58a1b4..8b8f4d7 100644 --- a/README +++ b/README @@ -330,6 +330,13 @@ tg export TODO: Make stripping of [PATCH] and other prefixes configurable TODO: --mbox option for other mode of operation +tg import +~~~~~~~~ + Import the commits between the given revision range into + a topgit managed branch + + Usage: tg import rev1..rev2 + tg update ~~~~~~~~~ Update the current topic branch wrt. changes in the branches diff --git a/tg-create.sh b/tg-create.sh index 939af33..6cce7ed 100644 --- a/tg-create.sh +++ b/tg-create.sh @@ -31,17 +31,18 @@ done deps="${deps# }" if [ -z "$deps" ]; then - if [ -z "$name" -a -s "$git_dir/top-name" -a -s "$git_dir/top-deps" -a -s "$git_dir/top-merge" ]; then - # We are setting up the base branch now; resume merge! - name="$(cat "$git_dir/top-name")" + head="$(git symbolic-ref HEAD)" + bname="${head#refs/top-bases/}" + if [ "$bname" != "$head" -a -s "$git_dir/top-deps" -a -s "$git_dir/top-merge" ]; then + # We are on a base branch now; resume merge! deps="$(cat "$git_dir/top-deps")" merge="$(cat "$git_dir/top-merge")" + name="$bname" restarted=1 info "Resuming $name setup..." else # The common case [ -z "$name" ] && die "no branch name given" - head="$(git symbolic-ref HEAD)" deps="${head#refs/heads/}" [ "$deps" != "$head" ] || die "refusing to auto-depend on non-head ref ($head)" info "Automatically marking dependency on $deps" @@ -58,7 +59,7 @@ done die "branch '$name' already exists" # Clean up any stale stuff -rm -f "$git_dir/top-name" "$git_dir/top-deps" "$git_dir/top-merge" +rm -f "$git_dir/top-deps" "$git_dir/top-merge" ## Create base @@ -68,8 +69,7 @@ if [ -n "$merge" ]; then branch="${merge%% *}" merge="${merge#* }" info "Creating $name base from $branch..." - # We create a detached head so that we can abort this operation - git checkout -q "$(git rev-parse "$branch")" + switch_to_base "$name" "$branch" fi @@ -83,10 +83,9 @@ while [ -n "$merge" ]; do if ! git merge "$branch"; then info "Please commit merge resolution and call: tg create" - info "It is also safe to abort this operation using:" - info "git reset --hard some_branch" - info "(You are on a detached HEAD now.)" - echo "$name" >"$git_dir/top-name" + info "It is also safe to abort this operation using \`git reset --hard\`" + info "but please remember you are on the base branch now;" + info "you will want to switch to a different branch." echo "$deps" >"$git_dir/top-deps" echo "$merge" >"$git_dir/top-merge" exit 2 @@ -96,7 +95,6 @@ done ## Set up the topic branch -git update-ref "refs/top-bases/$name" "HEAD" "" git checkout -b "$name" echo "$deps" | sed 's/ /\n/g' >"$root_dir/.topdeps" diff --git a/tg-export.sh b/tg-export.sh index 62361dd..73ad2ef 100644 --- a/tg-export.sh +++ b/tg-export.sh @@ -190,7 +190,7 @@ recurse_deps driver "$name" if [ "$driver" = "collapse" ]; then - git update-ref "refs/heads/$output" "$(cat "$playground/$name")" "" + git update-ref "refs/heads/$output" "$(cat "$playground/$name")" depcount="$(cat "$playground/^ticker" | wc -l)" echo "Exported topic branch $name (total $depcount topics) to branch $output" diff --git a/tg-import.sh b/tg-import.sh new file mode 100644 index 0000000..6c991c5 --- /dev/null +++ b/tg-import.sh @@ -0,0 +1,68 @@ +#!/bin/sh +# TopGit - A different patch queue manager +# GPLv2 + + +tg_get_commit_msg() +{ + commit="$1" + git log -1 --pretty=format:"From: %an <%ae>%n%n%s%n%n%b" "$commit" +} + +tg_get_branch_name() +{ + # nice sed script from git-format-patch.sh + commit="$1" + titleScript=' + s/[^-a-z.A-Z_0-9]/-/g + s/\.\.\.*/\./g + s/\.*$// + s/--*/-/g + s/^-// + s/-$// + q +' + git log -1 --pretty=format:"%s" "$commit" | sed -e "$titleScript" +} + +tg_process_commit() +{ + commit="$1" + branch_name=$(tg_get_branch_name "$commit") + echo "Importing $commit to $branch_name" + tg create tp/"$branch_name" + git read-tree "$commit" + tg_get_commit_msg "$commit" > .topmsg + git add -f .topmsg .topdeps + git commit -C "$commit" +} + +# nice arg verification stolen from git-format-patch.sh +for revpair +do + case "$revpair" in + ?*..?*) + rev1=`expr "z$revpair" : 'z\(.*\)\.\.'` + rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'` + ;; + *) + die "Unknow range spec $revpair" + ;; + esac + git rev-parse --verify "$rev1^0" >/dev/null 2>&1 || + die "Not a valid rev $rev1 ($revpair)" + git rev-parse --verify "$rev2^0" >/dev/null 2>&1 || + die "Not a valid rev $rev2 ($revpair)" + git cherry -v "$rev1" "$rev2" | + while read sign rev comment + do + case "$sign" in + '-') + info "Merged already: $comment" + ;; + *) + tg_process_commit "$rev" + ;; + esac + done +done -- tg: (f27e693..) tp/topgit-Implement-tg-import (depends on: master) ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2008-08-13 14:54 (unknown), aneesh.kumar @ 2008-08-13 15:16 ` Aneesh Kumar K.V 0 siblings, 0 replies; 34+ messages in thread From: Aneesh Kumar K.V @ 2008-08-13 15:16 UTC (permalink / raw) To: aneesh.kumar; +Cc: pasky, git On Wed, Aug 13, 2008 at 08:24:28PM +0530, aneesh.kumar@gmail.com wrote: > From: Aneesh Kumar K.V <aneesh.kumar@gmail.com> > > topgit: Implement tg-import > > This can be used to import a set of commits > between range specified by range1..range2 > This should help us to convert an already > existing quilt, stgit branches to topgit > managed one > > Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com> > > --- > Makefile | 2 +- > README | 7 ++++++ > tg-create.sh | 22 ++++++++---------- > tg-export.sh | 2 +- > tg-import.sh | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 87 insertions(+), 14 deletions(-) > tg-create.sh and tg-export.sh should not be part of this patch. I will send a new version. -aneesh ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 0/6] builtin-remote @ 2007-12-05 19:00 Johannes Schindelin 2007-12-05 19:00 ` (unknown) Johannes Schindelin 0 siblings, 1 reply; 34+ messages in thread From: Johannes Schindelin @ 2007-12-05 19:00 UTC (permalink / raw) To: git, gitster Hi, this series introduces remote as a builtin, and fixes a long-standing bug (if you created a remote with --mirror, prune would not do the right thing). I know it is pretty late in the game for 1.5.4, but then, I do not expect this to go into that version... Ciao, Dscho ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown) 2007-12-05 19:00 [PATCH 0/6] builtin-remote Johannes Schindelin @ 2007-12-05 19:00 ` Johannes Schindelin 2007-12-05 19:01 ` your mail Johannes Schindelin 0 siblings, 1 reply; 34+ messages in thread From: Johannes Schindelin @ 2007-12-05 19:00 UTC (permalink / raw) To: git, gitster [PATCH 1/6] path-list: add functions to work with unsorted lists Up to now, path-lists were sorted at all times. But sometimes it is much more convenient to build the list and sort it at the end, or sort it not at all. Add path_list_append() and sort_path_list() to allow that. Also, add the unsorted_path_list_has_path() function, to do a linear search. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- I should have done this much earlier... path-list.c | 30 ++++++++++++++++++++++++++++++ path-list.h | 8 +++++++- 2 files changed, 37 insertions(+), 1 deletions(-) diff --git a/path-list.c b/path-list.c index 3d83b7b..92e5cf2 100644 --- a/path-list.c +++ b/path-list.c @@ -102,3 +102,33 @@ void print_path_list(const char *text, const struct path_list *p) for (i = 0; i < p->nr; i++) printf("%s:%p\n", p->items[i].path, p->items[i].util); } + +struct path_list_item *path_list_append(const char *path, struct path_list *list) +{ + ALLOC_GROW(list->items, list->nr + 1, list->alloc); + list->items[list->nr].path = + list->strdup_paths ? xstrdup(path) : (char *)path; + return list->items + list->nr++; +} + +static int cmp_items(const void *a, const void *b) +{ + const struct path_list_item *one = a; + const struct path_list_item *two = b; + return strcmp(one->path, two->path); +} + +void sort_path_list(struct path_list *list) +{ + qsort(list->items, list->nr, sizeof(*list->items), cmp_items); +} + +int unsorted_path_list_has_path(struct path_list *list, const char *path) +{ + int i; + for (i = 0; i < list->nr; i++) + if (!strcmp(path, list->items[i].path)) + return 1; + return 0; +} + diff --git a/path-list.h b/path-list.h index 5931e2c..ca2cbba 100644 --- a/path-list.h +++ b/path-list.h @@ -13,10 +13,16 @@ struct path_list }; void print_path_list(const char *text, const struct path_list *p); +void path_list_clear(struct path_list *list, int free_util); +/* Use these functions only on sorted lists: */ int path_list_has_path(const struct path_list *list, const char *path); -void path_list_clear(struct path_list *list, int free_util); struct path_list_item *path_list_insert(const char *path, struct path_list *list); struct path_list_item *path_list_lookup(const char *path, struct path_list *list); +/* Use these functions only on unsorted lists: */ +struct path_list_item *path_list_append(const char *path, struct path_list *list); +void sort_path_list(struct path_list *list); +int unsorted_path_list_has_path(struct path_list *list, const char *path); + #endif /* PATH_LIST_H */ -- 1.5.3.7.2157.g9598e ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2007-12-05 19:00 ` (unknown) Johannes Schindelin @ 2007-12-05 19:01 ` Johannes Schindelin 0 siblings, 0 replies; 34+ messages in thread From: Johannes Schindelin @ 2007-12-05 19:01 UTC (permalink / raw) To: git, gitster Hi, On Wed, 5 Dec 2007, Johannes Schindelin wrote: > [PATCH 1/6] path-list: add functions to work with unsorted lists Ooops. Sorry, Dscho ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown),
@ 2007-10-13 4:01 Michael Witten
2007-10-13 4:07 ` your mail Jeff King
0 siblings, 1 reply; 34+ messages in thread
From: Michael Witten @ 2007-10-13 4:01 UTC (permalink / raw)
To: git; +Cc: Jeff King
I apologize if this is received twice.
I did add some comments, though!
On 12 Oct 2007, at 10:49:10 PM, Jeff King wrote:
> You are presumably doing a 'git-pull' on the cvsimport-ed commits. Try
> doing a git-rebase, which will filter out commits which make the same
> changes. Yes, it means throwing away your original commits, but the
> new
> ones should be morally equivalent (and are now the "official" upstream
> of the CVS repository).
Now that you mention it, I think the best approach would be to:
(1) cvsexportcommit
(2) git reset --hard LAST_CVS_IMPORT_AND_MERGE
(3) git cvsimport ..... # and merge
I think this is what you mean; it seems to me that rebasing isn't
quite that.
However, this will not preserve more complicated history such as merges
from another git repository.
Basically, I want to treat my git repository as the official
repository; the CVS
repo is just their for the old farts to get the latest stuff ;-P
Thanks!
Michael
PS
Please send me other opinions.
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2007-10-13 4:01 (unknown), Michael Witten @ 2007-10-13 4:07 ` Jeff King 0 siblings, 0 replies; 34+ messages in thread From: Jeff King @ 2007-10-13 4:07 UTC (permalink / raw) To: Michael Witten; +Cc: git On Sat, Oct 13, 2007 at 12:01:04AM -0400, Michael Witten wrote: > Now that you mention it, I think the best approach would be to: > > (1) cvsexportcommit > (2) git reset --hard LAST_CVS_IMPORT_AND_MERGE > (3) git cvsimport ..... # and merge > > I think this is what you mean; it seems to me that rebasing isn't quite > that. No, I mean rebasing instead of merge. As in, you have a history like this: /--C---D <-- your master A--B \--C'--D' <-- cvsimport merge tip where "C" and "D" are your commits in git, and C' and D' are pulled in from cvsimport. You want to rebase your work like this: A--B--C'--D'--C--D except that git-rebase is smart enough to realize that C == C' and skip it (so it's a "safe" way of moving forward). > However, this will not preserve more complicated history such as merges > from another git repository. Correct. Rebasing doesn't really handle merges, but I assumed you were just making simple commits on top of a cvs master. > Basically, I want to treat my git repository as the official > repository; the CVS repo is just their for the old farts to get the > latest stuff ;-P Then my suggestion doesn't really work. You might consider using git as the official server and letting the old farts use git-cvsserver. HTH, -Peff ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown) @ 2006-10-20 14:24 andyparkins 2006-10-20 14:42 ` your mail Johannes Schindelin 0 siblings, 1 reply; 34+ messages in thread From: andyparkins @ 2006-10-20 14:24 UTC (permalink / raw) >From 9c128bc4b9b85385b7b98ceae0c89283d70e5d45 Mon Sep 17 00:00:00 2001 From: Andy Parkins <andyparkins@gmail.com> Date: Fri, 20 Oct 2006 15:24:22 +0100 Subject: [PATCH] Remove git-send-email references from documentation MIME-Version: 1.0 X-TUID: 1fbae8e75caaf795 X-Length: 1933 To: git@vger.kernel.org Content-Type: Multipart/Mixed; boundary="Boundary-00=_WwNOFc8Re2ORHlu" Message-Id: <200610201524.22493.andyparkins@gmail.com> This is a multi-part message in MIME format. --Boundary-00=_WwNOFc8Re2ORHlu Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline git-send-email doesn't exist; so don't refer to it in the documentation. Perhaps git-send-email.perl is meant to do this job? It runs with an error. Signed-off-by: Andy Parkins <andyparkins@gmail.com> --- Documentation/git-format-patch.txt | 2 +- Documentation/git.txt | 3 --- 2 files changed, 1 insertions(+), 4 deletions(-) --Boundary-00=_WwNOFc8Re2ORHlu Content-Type: text/x-patch; name="9c128bc4b9b85385b7b98ceae0c89283d70e5d45.diff" Content-Transfer-Encoding: 8bit Content-Disposition: inline; filename="9c128bc4b9b85385b7b98ceae0c89283d70e5d45.diff" diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 67425dc..9257030 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -112,7 +112,7 @@ git-format-patch -M -B origin:: See Also -------- -gitlink:git-am[1], gitlink:git-send-email[1] +gitlink:git-am[1] Author diff --git a/Documentation/git.txt b/Documentation/git.txt index 3af6fc6..1f60d3f 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -478,9 +478,6 @@ gitlink:git-request-pull[1]:: gitlink:git-rev-parse[1]:: Pick out and massage parameters. -gitlink:git-send-email[1]:: - Send patch e-mails out of "format-patch --mbox" output. - gitlink:git-symbolic-ref[1]:: Read and modify symbolic refs. --Boundary-00=_WwNOFc8Re2ORHlu-- ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2006-10-20 14:24 (unknown) andyparkins @ 2006-10-20 14:42 ` Johannes Schindelin 0 siblings, 0 replies; 34+ messages in thread From: Johannes Schindelin @ 2006-10-20 14:42 UTC (permalink / raw) To: andyparkins; +Cc: git Hi, your email is horridly mixed here. I get an empty subject here, and the complete email header at the beginning of the email: On Fri, 20 Oct 2006, andyparkins@gmail.com wrote: > >From 9c128bc4b9b85385b7b98ceae0c89283d70e5d45 Mon Sep 17 00:00:00 2001 > From: Andy Parkins <andyparkins@gmail.com> > [... complete email header including MIME header ...] > Content-Disposition: inline > > git-send-email doesn't exist; so don't refer to it in the documentation. But it does! I just removed it, and "make" remade it. I don't even see in the Makefile why it should _not_ be made... Ciao, Dscho ^ permalink raw reply [flat|nested] 34+ messages in thread
[parent not found: <C8DBC54F2A9BAD4FA7F445CC7ADD963B0232474F@sslmexchange1.paymentech.us>]
* Re: your mail [not found] <C8DBC54F2A9BAD4FA7F445CC7ADD963B0232474F@sslmexchange1.paymentech.us> @ 2006-09-26 19:56 ` Linus Torvalds 0 siblings, 0 replies; 34+ messages in thread From: Linus Torvalds @ 2006-09-26 19:56 UTC (permalink / raw) To: Zhao, Jing; +Cc: Andy Whitcroft, Git Mailing List On Tue, 26 Sep 2006, Zhao, Jing wrote: > > I subscribed git emailing list (git@vger.kernel.org). I don't know for > what reason, my post emails all have been rejected. Could you post this > for me and shed some light on this issue? thanks, The vger.kernel.org lists have various spam detectors, and I suspect a combination of your email address and the signature you use just triggers that system to believe that you are trying to sell us house payment plans or something.. So I would suggest removing your signature in particular, that points to a web-site that is associated with an industry that has over-used the email medium for selling their services... > I tried to port git to VOS system (Stratus). When i compiled it, i > found it did not have 'regex.h' and its library. Do you know any > workaround for this problem? Or which package contains these code i can > port at first? I do not know if stratus has regex libraries anywhere, but googling for "VOS Stratus regex" seems to be saying that this isn't the first time that platform has had problems compiling various programs. I suspect you'd just have to compile one of the regex libraries that are out there as source. I think Henry Spencer's libraries are likely the canonical ones, but there's a "GNU regex" too, and that's probably the basis for the ones that are used in glibc. Dunno. Google for either of those, you'll find them. It's not new code, but I doubt it needs to be ;) Linus ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown) @ 2006-05-21 23:53 J. Bruce Fields 2006-05-21 23:55 ` your mail J. Bruce Fields 0 siblings, 1 reply; 34+ messages in thread From: J. Bruce Fields @ 2006-05-21 23:53 UTC (permalink / raw) To: Junio C Hamano; +Cc: git >From nobody Mon Sep 17 00:00:00 2001 From: J. Bruce Fields <bfields@citi.umich.edu> Date: Sun, 21 May 2006 16:52:34 -0400 Subject: [PATCH 1/3] tutorial: replace "whatchanged" by "log" Junio suggested changing references to git-whatchanged to git-log. Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> --- d1177b299b449e9eb63d963ee118a5e0283aa611 Documentation/tutorial.txt | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-) d1177b299b449e9eb63d963ee118a5e0283aa611 diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index fa79b01..cd0f0df 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -80,13 +80,13 @@ file; just remove it, then commit. At any point you can view the history of your changes using ------------------------------------------------ -$ git whatchanged +$ git log ------------------------------------------------ If you also want to see complete diffs at each step, use ------------------------------------------------ -$ git whatchanged -p +$ git log -p ------------------------------------------------ Managing branches @@ -216,7 +216,7 @@ This actually pulls changes from the bra "master". Alice could request a different branch by adding the name of the branch to the end of the git pull command line. -This merges Bob's changes into her repository; "git whatchanged" will +This merges Bob's changes into her repository; "git log" will now show the new commits. If Alice has made her own changes in the meantime, then Bob's changes will be merged in, and she will need to manually fix any conflicts. @@ -234,7 +234,7 @@ named bob-incoming. (Unlike git pull, g of Bob's line of development without doing any merging). Then ------------------------------------- -$ git whatchanged -p master..bob-incoming +$ git log -p master..bob-incoming ------------------------------------- shows a list of all the changes that Bob made since he branched from @@ -330,13 +330,13 @@ But you may find it more useful to see t the experimental branch but not in the current branch, and ------------------------------------- -git whatchanged HEAD..experimental +git log HEAD..experimental ------------------------------------- will do that, just as ------------------------------------- -git whatchanged experimental..HEAD +git log experimental..HEAD ------------------------------------- will show the list of commits made on the HEAD but not included in -- 1.3.3.gff62 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2006-05-21 23:53 (unknown) J. Bruce Fields @ 2006-05-21 23:55 ` J. Bruce Fields 2006-05-22 0:16 ` Junio C Hamano 0 siblings, 1 reply; 34+ messages in thread From: J. Bruce Fields @ 2006-05-21 23:55 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Sun, May 21, 2006 at 07:53:18PM -0400, J. Bruce Fields wrote: > >From nobody Mon Sep 17 00:00:00 2001 > From: J. Bruce Fields <bfields@citi.umich.edu> Oops, sorry, I screwed up sending those; let me know if you'd like them resent.... --b. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2006-05-21 23:55 ` your mail J. Bruce Fields @ 2006-05-22 0:16 ` Junio C Hamano 2006-05-22 1:33 ` J. Bruce Fields 0 siblings, 1 reply; 34+ messages in thread From: Junio C Hamano @ 2006-05-22 0:16 UTC (permalink / raw) To: J. Bruce Fields; +Cc: git "J. Bruce Fields" <bfields@fieldses.org> writes: > On Sun, May 21, 2006 at 07:53:18PM -0400, J. Bruce Fields wrote: >> >From nobody Mon Sep 17 00:00:00 2001 >> From: J. Bruce Fields <bfields@citi.umich.edu> > > Oops, sorry, I screwed up sending those; let me know if you'd like them > resent.... That's OK. I just cooked up this one ;-). -- >8 -- From 03946787890c12fbb6ecfbe0382cbf02ac209801 Mon Sep 17 00:00:00 2001 From: Junio C Hamano <junkio@cox.net> Date: Sun, 21 May 2006 17:15:06 -0700 Subject: [PATCH] mailinfo: skip bogus UNIX From line inside body Sometimes people just include the whole format-patch output in the commit e-mail. Detect it and skip the bogus ">From " line. Signed-off-by: Junio C Hamano <junkio@cox.net> --- mailinfo.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/mailinfo.c b/mailinfo.c index b276519..a133e6d 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -237,10 +237,17 @@ static int eatspace(char *line) #define SEEN_FROM 01 #define SEEN_DATE 02 #define SEEN_SUBJECT 04 +#define SEEN_BOGUS_UNIX_FROM 010 /* First lines of body can have From:, Date:, and Subject: */ static int handle_inbody_header(int *seen, char *line) { + if (!memcmp(">From", line, 5) && isspace(line[5])) { + if (!(*seen & SEEN_BOGUS_UNIX_FROM)) { + *seen |= SEEN_BOGUS_UNIX_FROM; + return 1; + } + } if (!memcmp("From:", line, 5) && isspace(line[5])) { if (!(*seen & SEEN_FROM) && handle_from(line+6)) { *seen |= SEEN_FROM; -- 1.3.3.g292f ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: your mail 2006-05-22 0:16 ` Junio C Hamano @ 2006-05-22 1:33 ` J. Bruce Fields 0 siblings, 0 replies; 34+ messages in thread From: J. Bruce Fields @ 2006-05-22 1:33 UTC (permalink / raw) To: Junio C Hamano; +Cc: git On Sun, May 21, 2006 at 05:16:44PM -0700, Junio C Hamano wrote: > "J. Bruce Fields" <bfields@fieldses.org> writes: > > > On Sun, May 21, 2006 at 07:53:18PM -0400, J. Bruce Fields wrote: > >> >From nobody Mon Sep 17 00:00:00 2001 > >> From: J. Bruce Fields <bfields@citi.umich.edu> > > > > Oops, sorry, I screwed up sending those; let me know if you'd like them > > resent.... > > That's OK. I just cooked up this one ;-). Thanks!--b. ^ permalink raw reply [flat|nested] 34+ messages in thread
* (unknown), @ 2005-10-05 6:10 Willem Swart 2005-10-06 10:52 ` your mail Elfyn McBratney 0 siblings, 1 reply; 34+ messages in thread From: Willem Swart @ 2005-10-05 6:10 UTC (permalink / raw) To: git subscribe git ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: your mail 2005-10-05 6:10 (unknown), Willem Swart @ 2005-10-06 10:52 ` Elfyn McBratney 0 siblings, 0 replies; 34+ messages in thread From: Elfyn McBratney @ 2005-10-06 10:52 UTC (permalink / raw) To: git; +Cc: Willem Swart [-- Attachment #1: Type: text/plain, Size: 701 bytes --] On Wed, Oct 05, 2005 at 08:10:24AM +0200, Willem Swart wrote: > subscribe git > - > To unsubscribe from this list: send the line "unsubscribe git" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html s/unsubscribe/subscribe/g Might want to re-send that to majordomo@vger.kernel.org ;) Best, Elfyn -- Elfyn McBratney Gentoo Developer/Perl Team Lead beu/irc.freenode.net http://dev.gentoo.org/~beu/ +------------O.o--------------------- http://dev.gentoo.org/~beu/pubkey.asc PGP Key ID: 0x69DF17AD PGP Key Fingerprint: DBD3 B756 ED58 B1B4 47B9 B3BD 8D41 E597 69DF 17AD [-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2020-06-24 1:31 UTC | newest] Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2006-11-21 22:24 (unknown) Johannes Schindelin 2006-11-21 23:15 ` [PATCH] xdiff: add xdl_merge() (was: (unknown)) Jakub Narebski 2006-11-22 9:29 ` Johannes Schindelin 2006-11-22 19:58 ` [PATCH] xdiff: add xdl_merge() Ramsay Jones 2006-11-22 20:16 ` your mail Davide Libenzi -- strict thread matches above, loose matches on Subject: below -- 2020-06-24 0:38 shejan shuza 2020-06-24 1:31 ` your mail brian m. carlson 2019-11-20 3:49 Han-Wen Nienhuys 2019-11-20 5:30 ` your mail Taylor Blau 2019-11-20 8:05 ` Christian Couder 2019-07-11 20:11 Robert Morgan 2019-07-11 20:18 ` your mail Kevin Daudt 2019-07-11 20:25 ` Robert Morgan 2019-01-25 9:47 Furkan DURUL 2019-01-25 11:27 ` your mail Kevin Daudt 2017-06-22 9:50 Jessie Hernandez 2017-06-22 12:48 ` your mail Simon Ruderich 2017-06-22 13:35 ` AW: " Patrick Lehmann 2017-06-22 13:47 ` Simon Ruderich 2017-06-22 13:55 ` AW: " Patrick Lehmann 2017-06-22 20:46 ` Simon Ruderich 2017-06-22 21:35 ` Junio C Hamano 2017-06-22 21:58 ` Ævar Arnfjörð Bjarmason 2017-06-22 22:14 ` Junio C Hamano 2017-06-22 23:21 ` Jeff King 2017-06-23 5:23 ` Junio C Hamano 2017-06-23 16:53 ` Jeff King 2017-06-23 18:44 ` Junio C Hamano 2017-06-23 6:58 ` demerphq 2016-04-11 19:04 (unknown), miwilliams 2016-04-11 19:13 ` your mail Jeff King 2013-05-17 18:02 (unknown), ASHISH VERMA 2013-05-21 13:13 ` your mail Magnus Bäck 2012-05-06 14:17 (unknown), Bruce Zu 2012-05-06 17:04 ` your mail Marcus Karlsson 2008-08-13 14:54 (unknown), aneesh.kumar 2008-08-13 15:16 ` your mail Aneesh Kumar K.V 2007-12-05 19:00 [PATCH 0/6] builtin-remote Johannes Schindelin 2007-12-05 19:00 ` (unknown) Johannes Schindelin 2007-12-05 19:01 ` your mail Johannes Schindelin 2007-10-13 4:01 (unknown), Michael Witten 2007-10-13 4:07 ` your mail Jeff King 2006-10-20 14:24 (unknown) andyparkins 2006-10-20 14:42 ` your mail Johannes Schindelin [not found] <C8DBC54F2A9BAD4FA7F445CC7ADD963B0232474F@sslmexchange1.paymentech.us> 2006-09-26 19:56 ` Linus Torvalds 2006-05-21 23:53 (unknown) J. Bruce Fields 2006-05-21 23:55 ` your mail J. Bruce Fields 2006-05-22 0:16 ` Junio C Hamano 2006-05-22 1:33 ` J. Bruce Fields 2005-10-05 6:10 (unknown), Willem Swart 2005-10-06 10:52 ` your mail Elfyn McBratney
Code repositories for project(s) associated with this public inbox https://80x24.org/mirrors/git.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).