* Getting 'git log' (or something else) to show me the relevant sub-graph? @ 2010-04-20 14:49 Johan Herland 2010-04-20 15:18 ` Michael J Gruber 2010-04-20 20:45 ` Junio C Hamano 0 siblings, 2 replies; 11+ messages in thread From: Johan Herland @ 2010-04-20 14:49 UTC (permalink / raw) To: git Hi, Consider the following (simplified) history: ---O---O---O---O---O---O---O---O---O---O---O---O---M2--H <-- mainline \ / O---O---O---O---O---O---O---M1--D---E---F---G <-- dev-branch \ / O---O---A---B---C <-- topic-branch Now, assume that I have bisected my way through to 'A', and found that it introduces some bug. Now, I'm interested in visualizing the path that this bug "travelled" to get into "mainline", i.e. the following sub-graph: --M2--H <-- mainline / --M1--D---E---F---G <-- dev-branch / A---B---C <-- topic-branch In other words, I'm interested in the following log (with decorations): H (mainline) M2 G (dev-branch) F E D M1 C (topic-branch) B A I have unsuccessfully dug through the 'git log' documentation to figure out if it can produce this log, so I'm now throwing the question to the almighty knowledge of the mailing list... Here are some of my closest attempts, so far: - git branch --contains A gives me "topic-branch", "dev-branch" and "mainline", which is relevant, but incomplete. - git log --oneline --decorate --graph A^..mainline gives me a log/graph where I can search for A and then use the graph to trace the way back up to "mainline", but it still displays a lot of uninteresting commits (ancestors of M1 and M2) that I have to disregard. Although this is ok once in a while, the problem is common enough (and the real-world graphs complicated enough), that I'd like a better solution, if possible. I guess what I'm looking for is something similar to --first-parent, except instead of the _first_ parent, it should follow the _relevant_ parent, as far as the relationship between A and "mainline" is concerned. In set-theory terms I guess what I want is "that which is both an ancestor of H, and a descendant of A (inclusive)", but I don't know how to explain this to 'git log'. Thanks for any help, ...Johan -- Johan Herland, <johan@herland.net> www.herland.net ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Getting 'git log' (or something else) to show me the relevant sub-graph? 2010-04-20 14:49 Getting 'git log' (or something else) to show me the relevant sub-graph? Johan Herland @ 2010-04-20 15:18 ` Michael J Gruber 2010-04-20 20:45 ` Junio C Hamano 1 sibling, 0 replies; 11+ messages in thread From: Michael J Gruber @ 2010-04-20 15:18 UTC (permalink / raw) To: Johan Herland; +Cc: git Johan Herland venit, vidit, dixit 20.04.2010 16:49: > Hi, > > Consider the following (simplified) history: > > ---O---O---O---O---O---O---O---O---O---O---O---O---M2--H <-- mainline > \ / > O---O---O---O---O---O---O---M1--D---E---F---G <-- dev-branch > \ / > O---O---A---B---C <-- topic-branch > > Now, assume that I have bisected my way through to 'A', and found that > it introduces some bug. Now, I'm interested in visualizing the path > that this bug "travelled" to get into "mainline", i.e. the following > sub-graph: > > --M2--H <-- mainline > / > --M1--D---E---F---G <-- dev-branch > / > A---B---C <-- topic-branch > > In other words, I'm interested in the following log (with decorations): > > H (mainline) > M2 > G (dev-branch) > F > E > D > M1 > C (topic-branch) > B > A > > I have unsuccessfully dug through the 'git log' documentation to figure > out if it can produce this log, so I'm now throwing the question to the > almighty knowledge of the mailing list... > > Here are some of my closest attempts, so far: > > - git branch --contains A > gives me "topic-branch", "dev-branch" and "mainline", which is > relevant, but incomplete. > > - git log --oneline --decorate --graph A^..mainline > gives me a log/graph where I can search for A and then use the graph > to trace the way back up to "mainline", but it still displays a lot of > uninteresting commits (ancestors of M1 and M2) that I have to > disregard. Although this is ok once in a while, the problem is common > enough (and the real-world graphs complicated enough), that I'd like a > better solution, if possible. > > I guess what I'm looking for is something similar to --first-parent, > except instead of the _first_ parent, it should follow the _relevant_ > parent, as far as the relationship between A and "mainline" is > concerned. > > In set-theory terms I guess what I want is "that which is both an > ancestor of H, and a descendant of A (inclusive)", but I don't know how > to explain this to 'git log'. This would require a reverse tree walker for the first half of that set intersection. It would come in handy in many situations, but we don't have it. Mathematically speaking it is trivial to associate to a directed graph the graph with opposite orientation. Then we could walk as usual... But, in fact, we do not "walk then intersect", but the walker implements the intersection, so having that other dag would not even help. Maybe we can teach the walker to mark commits as uninteresting which do not have A in their ancestry? Michael ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: Getting 'git log' (or something else) to show me the relevant sub-graph? 2010-04-20 14:49 Getting 'git log' (or something else) to show me the relevant sub-graph? Johan Herland 2010-04-20 15:18 ` Michael J Gruber @ 2010-04-20 20:45 ` Junio C Hamano 2010-04-20 22:10 ` [PATCH] revision: --ancestry-path Junio C Hamano 1 sibling, 1 reply; 11+ messages in thread From: Junio C Hamano @ 2010-04-20 20:45 UTC (permalink / raw) To: Johan Herland; +Cc: git Johan Herland <johan@herland.net> writes: > Now, assume that I have bisected my way through to 'A', and found that > it introduces some bug. Now, I'm interested in visualizing the path > that this bug "travelled" to get into "mainline", i.e. the following > sub-graph: > > --M2--H <-- mainline > / > --M1--D---E---F---G <-- dev-branch > / > A---B---C <-- topic-branch Interesting. I am looking into it ;-) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] revision: --ancestry-path 2010-04-20 20:45 ` Junio C Hamano @ 2010-04-20 22:10 ` Junio C Hamano 2010-04-21 7:34 ` Johan Herland 0 siblings, 1 reply; 11+ messages in thread From: Junio C Hamano @ 2010-04-20 22:10 UTC (permalink / raw) To: Johan Herland; +Cc: git, Michael J Gruber This is somewhat a rough-cut, as I haven't thought through possible interactions this new option may have with "rev-list --parents" and "rev-list --graph" (see "NEEDSWORK" in revision.c), but I'll leave that to people who are more interested in this topic than myself ;-). -- >8 -- "rev-list A..H" computes the set of commits that are ancestors of H, but excludes the ones that are ancestors of A. This is useful to see what happened to the history leading to H since A, in the sense that "what does H have that did not exist in A" (e.g. when you have a choice to update to H from A). x---x---A---B---C <-- topic / \ x---x---x---o---o---o---o---M---D---E---F---G <-- dev / \ x---o---o---o---o---o---o---o---o---o---o---o---N---H <-- master The result in the above example would be the commits marked with caps letters (except for A itself, of course), and the ones marked with 'o'. When you want to find out what commits in H are contaminated with the bug introduced by A and need fixing, however, you might want to view only the subset of "A..B" that are actually descendants of A, i.e. excluding the ones marked with 'o'. Introduce a new option --ancestry-path to compute this set with "rev-list --ancestry-path A..B". Note that in practice, you would build a fix immediately on top of A and "git branch --contains A" will give the names of branches that you would need to merge the fix into (i.e. topic, dev and master), so this may not be worth paying the extra cost of postprocessing. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- revision.c | 106 +++++++++++++++++++++++++++++++++++++ revision.h | 1 + t/t6019-rev-list-ancestry-path.sh | 56 +++++++++++++++++++ 3 files changed, 163 insertions(+), 0 deletions(-) create mode 100755 t/t6019-rev-list-ancestry-path.sh diff --git a/revision.c b/revision.c index f4b8b38..cb91094 100644 --- a/revision.c +++ b/revision.c @@ -646,6 +646,99 @@ static int still_interesting(struct commit_list *src, unsigned long date, int sl return slop-1; } +/* + * "rev-list --ancestry-path A..B" computes commits that are ancestors + * of B but not ancestors of A but further limits the result to those + * that are descendants of A. This takes the list of bottom commits and + * the result of "A..B" without --ancestry-path, and limits the latter + * further to the ones that can reach one of the commits in "bottom". + */ +static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list) +{ + struct commit_list *p; + struct commit_list *rlist = NULL; + int made_progress; + + /* + * Clear TMP_MARK and reverse the list so that it will be likely + * that we would process parents before children. + */ + for (p = list; p; p = p->next) { + p->item->object.flags &= ~TMP_MARK; + commit_list_insert(p->item, &rlist); + } + + for (p = bottom; p; p = p->next) + p->item->object.flags |= TMP_MARK; + + /* + * Mark the ones that can reach bottom commits in "list", + * in a bottom-up fashion. + */ + do { + made_progress = 0; + for (p = rlist; p; p = p->next) { + struct commit *c = p->item; + struct commit_list *parents; + if (c->object.flags & (TMP_MARK | UNINTERESTING)) + continue; + for (parents = c->parents; + parents; + parents = parents->next) { + if (!(parents->item->object.flags & TMP_MARK)) + continue; + c->object.flags |= TMP_MARK; + made_progress = 1; + break; + } + } + } while (made_progress); + + /* + * NEEDSWORK: decide if we want to remove parents that are + * not marked with TMP_MARK from commit->parents for commits + * in the resulting list. We may not want to do that, though. + */ + + /* + * The ones that are not marked with TMP_MARK are uninteresting + */ + for (p = list; p; p = p->next) { + struct commit *c = p->item; + if (c->object.flags & TMP_MARK) { + c->object.flags &= ~TMP_MARK; + continue; + } + c->object.flags |= UNINTERESTING; + } + + /* Release the bottom list */ + while (bottom) { + p = bottom->next; + bottom->item->object.flags &= ~TMP_MARK; + free(bottom); + bottom = p; + } + free_commit_list(rlist); +} + +/* + * Before walking the history, keep the set of "negative" refs the + * caller has asked to exclude. + * + * This is used to compute "rev-list --ancestry-path A..B", as we need + * to filter the result of "A..B" further to the ones that can actually + * reach A. + */ +static struct commit_list *collect_bottom_commits(struct commit_list *list) +{ + struct commit_list *elem, *bottom = NULL; + for (elem = list; elem; elem = elem->next) + if (elem->item->object.flags & UNINTERESTING) + commit_list_insert(elem->item, &bottom); + return bottom; +} + static int limit_list(struct rev_info *revs) { int slop = SLOP; @@ -653,6 +746,13 @@ static int limit_list(struct rev_info *revs) struct commit_list *list = revs->commits; struct commit_list *newlist = NULL; struct commit_list **p = &newlist; + struct commit_list *bottom = NULL; + + if (revs->ancestry_path) { + bottom = collect_bottom_commits(list); + if (!bottom) + die("--ancestry-path given but there is no bottom commits"); + } while (list) { struct commit_list *entry = list; @@ -694,6 +794,9 @@ static int limit_list(struct rev_info *revs) if (revs->cherry_pick) cherry_pick_list(newlist, revs); + if (bottom) + limit_to_ancestry(bottom, newlist); + revs->commits = newlist; return 0; } @@ -1089,6 +1192,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->min_age = approxidate(arg + 8); } else if (!strcmp(arg, "--first-parent")) { revs->first_parent_only = 1; + } else if (!strcmp(arg, "--ancestry-path")) { + revs->ancestry_path = 1; + revs->limited = 1; } else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) { init_reflog_walk(&revs->reflog_info); } else if (!strcmp(arg, "--default")) { diff --git a/revision.h b/revision.h index 568f1c9..855464f 100644 --- a/revision.h +++ b/revision.h @@ -66,6 +66,7 @@ struct rev_info { reverse_output_stage:1, cherry_pick:1, bisect:1, + ancestry_path:1, first_parent_only:1; /* Diff flags */ diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh new file mode 100755 index 0000000..0230724 --- /dev/null +++ b/t/t6019-rev-list-ancestry-path.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +test_description='--ancestry-path' + +# D---E-------F +# / \ \ +# B---C---G---H---I---J +# / \ +# A-------K---------------L--M +# +# D..M == E F G H I J K L M +# --ancestry-path D..M == E F H I J L M + +. ./test-lib.sh + +test_merge () { + test_tick && + git merge -s ours -m "$2" "$1" && + git tag "$2" +} + +test_expect_success setup ' + test_commit A && + test_commit B && + test_commit C && + test_commit D && + test_commit E && + test_commit F && + git reset --hard C && + test_commit G && + test_merge E H && + test_commit I && + test_merge F J && + git reset --hard A && + test_commit K && + test_merge J L && + test_commit M +' + +test_expect_success 'rev-list D..M' ' + for c in E F G H I J K L M; do echo $c; done >expect && + git rev-list --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list --ancestry-path D..M' ' + for c in E F H I J L M; do echo $c; done >expect && + git rev-list --ancestry-path --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' + +test_done -- 1.7.1.rc2.265.g8743f ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] revision: --ancestry-path 2010-04-20 22:10 ` [PATCH] revision: --ancestry-path Junio C Hamano @ 2010-04-21 7:34 ` Johan Herland 2010-04-21 7:47 ` Johannes Sixt 0 siblings, 1 reply; 11+ messages in thread From: Johan Herland @ 2010-04-21 7:34 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Michael J Gruber On Wednesday 21 April 2010, Junio C Hamano wrote: > This is somewhat a rough-cut, as I haven't thought through possible > interactions this new option may have with "rev-list --parents" and > "rev-list --graph" (see "NEEDSWORK" in revision.c), but I'll leave that > to people who are more interested in this topic than myself ;-). Thanks! :) I can confirm that this patch works with my original (more complicated) scenario. I've also played around with combinations of --ancestry-path, -- graph and --parents (and even --boundary), and AFAICS, the new option does not clobber the other options, and (IMHO) it all behaves correctly in the scenarios I've tested. Consider it: Tested-by: Johan Herland <johan@herland.net> I've also reviewed the patch, and it all looks good to me (i.e. "Acked-by", except I'm not super-familiar in the relevant area of the code). What more should I do to push this forwards towards 'next'? ...Johan -- Johan Herland, <johan@herland.net> www.herland.net ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] revision: --ancestry-path 2010-04-21 7:34 ` Johan Herland @ 2010-04-21 7:47 ` Johannes Sixt 2010-04-21 8:01 ` Junio C Hamano 0 siblings, 1 reply; 11+ messages in thread From: Johannes Sixt @ 2010-04-21 7:47 UTC (permalink / raw) To: Johan Herland; +Cc: Junio C Hamano, git, Michael J Gruber Am 4/21/2010 9:34, schrieb Johan Herland: > I can confirm that this patch works with my original (more complicated) > scenario. I've also played around with combinations of --ancestry-path, -- > graph and --parents (and even --boundary), and AFAICS, the new option does > not clobber the other options, and (IMHO) it all behaves correctly in the > scenarios I've tested. How about possible interactions with --reverse; --date-order/--topo-order, --parents (important for gitk); --simplify-by-decoration (useful for your problem that triggered this); --full-history and --simplify-merges with path limiting? -- Hannes ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] revision: --ancestry-path 2010-04-21 7:47 ` Johannes Sixt @ 2010-04-21 8:01 ` Junio C Hamano 2010-04-21 8:19 ` [PATCH v2] " Junio C Hamano ` (2 more replies) 0 siblings, 3 replies; 11+ messages in thread From: Junio C Hamano @ 2010-04-21 8:01 UTC (permalink / raw) To: Johannes Sixt; +Cc: Johan Herland, Junio C Hamano, git, Michael J Gruber Johannes Sixt <j.sixt@viscovery.net> writes: > Am 4/21/2010 9:34, schrieb Johan Herland: >> I can confirm that this patch works with my original (more complicated) >> scenario. I've also played around with combinations of --ancestry-path, -- >> graph and --parents (and even --boundary), and AFAICS, the new option does >> not clobber the other options, and (IMHO) it all behaves correctly in the >> scenarios I've tested. > > How about possible interactions with --reverse; --date-order/--topo-order, > --parents (important for gitk); --simplify-by-decoration (useful for your > problem that triggered this); --full-history and --simplify-merges with > path limiting? These are all good points. I am reasonably sure that parents (specifically, "rewrite_parents") is broken. The new function should cull parents that do not appear on the ancestry path from merges (that is what "NEEDSWORK" is about). It may or may not break gitk, though---these off-path parents are shown as parents of an on-path merge but will be marked as UNINTERESTING. I do not think reverse/date-order/topo-order would be affected by this change, as they only change the presentation order of what limit_list() returns; Also simplify-merges and full-history should be Ok, as they control what is done in the current logic in limit_list(), which is an input to the new logic, meaning that the new logic will work on already simplified history. This is not a new problem, but I strongly suspect that cherry-pick is broken the same way wrt "rewrite_parents". ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2] revision: --ancestry-path 2010-04-21 8:01 ` Junio C Hamano @ 2010-04-21 8:19 ` Junio C Hamano 2010-04-21 8:46 ` [PATCH] " Johan Herland 2010-04-21 8:49 ` Junio C Hamano 2 siblings, 0 replies; 11+ messages in thread From: Junio C Hamano @ 2010-04-21 8:19 UTC (permalink / raw) To: Johannes Sixt, Johan Herland; +Cc: git, Michael J Gruber "rev-list A..H" computes the set of commits that are ancestors of H, but excludes the ones that are ancestors of A. This is useful to see what happened to the history leading to H since A, in the sense that "what does H have that did not exist in A" (e.g. when you have a choice to update to H from A). x---x---A---B---C <-- topic / \ x---x---x---o---o---o---o---M---D---E---F---G <-- dev / \ x---o---o---o---o---o---o---o---o---o---o---o---N---H <-- master The result in the above example would be the commits marked with caps letters (except for A itself, of course), and the ones marked with 'o'. When you want to find out what commits in H are contaminated with the bug introduced by A and need fixing, however, you might want to view only the subset of "A..B" that are actually descendants of A, i.e. excluding the ones marked with 'o'. Introduce a new option --ancestry-path to compute this set with "rev-list --ancestry-path A..B". Note that in practice, you would build a fix immediately on top of A and "git branch --contains A" will give the names of branches that you would need to merge the fix into (i.e. topic, dev and master), so this may not be worth paying the extra cost of postprocessing. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- * This simplifies the handling of TMP_MARK (the contract among the functions is that this is used only locally and the user is responsible for cleaning them up after use, so there is no need to clear the mark upfront), and also makes it responsibility of the caller to free the "bottom" list. Interdiff since v1 looks like this. diff -u b/revision.c b/revision.c --- b/revision.c +++ b/revision.c @@ -660,13 +660,11 @@ int made_progress; /* - * Clear TMP_MARK and reverse the list so that it will be likely - * that we would process parents before children. + * Reverse the list so that it will be likely that we would + * process parents before children. */ - for (p = list; p; p = p->next) { - p->item->object.flags &= ~TMP_MARK; + for (p = list; p; p = p->next) commit_list_insert(p->item, &rlist); - } for (p = bottom; p; p = p->next) p->item->object.flags |= TMP_MARK; @@ -705,20 +703,16 @@ */ for (p = list; p; p = p->next) { struct commit *c = p->item; - if (c->object.flags & TMP_MARK) { - c->object.flags &= ~TMP_MARK; + if (c->object.flags & TMP_MARK) continue; - } c->object.flags |= UNINTERESTING; } - /* Release the bottom list */ - while (bottom) { - p = bottom->next; - bottom->item->object.flags &= ~TMP_MARK; - free(bottom); - bottom = p; - } + /* We are done with the TMP_MARK */ + for (p = list; p; p = p->next) + p->item->object.flags &= ~TMP_MARK; + for (p = bottom; p; p = p->next) + p->item->object.flags &= ~TMP_MARK; free_commit_list(rlist); } @@ -794,8 +788,10 @@ if (revs->cherry_pick) cherry_pick_list(newlist, revs); - if (bottom) + if (bottom) { limit_to_ancestry(bottom, newlist); + free_commit_list(bottom); + } revs->commits = newlist; return 0; revision.c | 102 +++++++++++++++++++++++++++++++++++++ revision.h | 1 + t/t6019-rev-list-ancestry-path.sh | 56 ++++++++++++++++++++ 3 files changed, 159 insertions(+), 0 deletions(-) create mode 100755 t/t6019-rev-list-ancestry-path.sh diff --git a/revision.c b/revision.c index f4b8b38..71fec3c 100644 --- a/revision.c +++ b/revision.c @@ -646,6 +646,93 @@ static int still_interesting(struct commit_list *src, unsigned long date, int sl return slop-1; } +/* + * "rev-list --ancestry-path A..B" computes commits that are ancestors + * of B but not ancestors of A but further limits the result to those + * that are descendants of A. This takes the list of bottom commits and + * the result of "A..B" without --ancestry-path, and limits the latter + * further to the ones that can reach one of the commits in "bottom". + */ +static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list) +{ + struct commit_list *p; + struct commit_list *rlist = NULL; + int made_progress; + + /* + * Reverse the list so that it will be likely that we would + * process parents before children. + */ + for (p = list; p; p = p->next) + commit_list_insert(p->item, &rlist); + + for (p = bottom; p; p = p->next) + p->item->object.flags |= TMP_MARK; + + /* + * Mark the ones that can reach bottom commits in "list", + * in a bottom-up fashion. + */ + do { + made_progress = 0; + for (p = rlist; p; p = p->next) { + struct commit *c = p->item; + struct commit_list *parents; + if (c->object.flags & (TMP_MARK | UNINTERESTING)) + continue; + for (parents = c->parents; + parents; + parents = parents->next) { + if (!(parents->item->object.flags & TMP_MARK)) + continue; + c->object.flags |= TMP_MARK; + made_progress = 1; + break; + } + } + } while (made_progress); + + /* + * NEEDSWORK: decide if we want to remove parents that are + * not marked with TMP_MARK from commit->parents for commits + * in the resulting list. We may not want to do that, though. + */ + + /* + * The ones that are not marked with TMP_MARK are uninteresting + */ + for (p = list; p; p = p->next) { + struct commit *c = p->item; + if (c->object.flags & TMP_MARK) + continue; + c->object.flags |= UNINTERESTING; + } + + /* We are done with the TMP_MARK */ + for (p = list; p; p = p->next) + p->item->object.flags &= ~TMP_MARK; + for (p = bottom; p; p = p->next) + p->item->object.flags &= ~TMP_MARK; + free_commit_list(rlist); +} + +/* + * Before walking the history, keep the set of "negative" refs the + * caller has asked to exclude. + * + * This is used to compute "rev-list --ancestry-path A..B", as we need + * to filter the result of "A..B" further to the ones that can actually + * reach A. + */ +static struct commit_list *collect_bottom_commits(struct commit_list *list) +{ + struct commit_list *elem, *bottom = NULL; + for (elem = list; elem; elem = elem->next) + if (elem->item->object.flags & UNINTERESTING) + commit_list_insert(elem->item, &bottom); + return bottom; +} + static int limit_list(struct rev_info *revs) { int slop = SLOP; @@ -653,6 +740,13 @@ static int limit_list(struct rev_info *revs) struct commit_list *list = revs->commits; struct commit_list *newlist = NULL; struct commit_list **p = &newlist; + struct commit_list *bottom = NULL; + + if (revs->ancestry_path) { + bottom = collect_bottom_commits(list); + if (!bottom) + die("--ancestry-path given but there is no bottom commits"); + } while (list) { struct commit_list *entry = list; @@ -694,6 +788,11 @@ static int limit_list(struct rev_info *revs) if (revs->cherry_pick) cherry_pick_list(newlist, revs); + if (bottom) { + limit_to_ancestry(bottom, newlist); + free_commit_list(bottom); + } + revs->commits = newlist; return 0; } @@ -1089,6 +1188,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->min_age = approxidate(arg + 8); } else if (!strcmp(arg, "--first-parent")) { revs->first_parent_only = 1; + } else if (!strcmp(arg, "--ancestry-path")) { + revs->ancestry_path = 1; + revs->limited = 1; } else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) { init_reflog_walk(&revs->reflog_info); } else if (!strcmp(arg, "--default")) { diff --git a/revision.h b/revision.h index 568f1c9..855464f 100644 --- a/revision.h +++ b/revision.h @@ -66,6 +66,7 @@ struct rev_info { reverse_output_stage:1, cherry_pick:1, bisect:1, + ancestry_path:1, first_parent_only:1; /* Diff flags */ diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh new file mode 100755 index 0000000..0230724 --- /dev/null +++ b/t/t6019-rev-list-ancestry-path.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +test_description='--ancestry-path' + +# D---E-------F +# / \ \ +# B---C---G---H---I---J +# / \ +# A-------K---------------L--M +# +# D..M == E F G H I J K L M +# --ancestry-path D..M == E F H I J L M + +. ./test-lib.sh + +test_merge () { + test_tick && + git merge -s ours -m "$2" "$1" && + git tag "$2" +} + +test_expect_success setup ' + test_commit A && + test_commit B && + test_commit C && + test_commit D && + test_commit E && + test_commit F && + git reset --hard C && + test_commit G && + test_merge E H && + test_commit I && + test_merge F J && + git reset --hard A && + test_commit K && + test_merge J L && + test_commit M +' + +test_expect_success 'rev-list D..M' ' + for c in E F G H I J K L M; do echo $c; done >expect && + git rev-list --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list --ancestry-path D..M' ' + for c in E F H I J L M; do echo $c; done >expect && + git rev-list --ancestry-path --format=%s D..M | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' + +test_done -- 1.7.1.rc2.265.g8743f ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] revision: --ancestry-path 2010-04-21 8:01 ` Junio C Hamano 2010-04-21 8:19 ` [PATCH v2] " Junio C Hamano @ 2010-04-21 8:46 ` Johan Herland 2010-04-21 9:04 ` Junio C Hamano 2010-04-21 8:49 ` Junio C Hamano 2 siblings, 1 reply; 11+ messages in thread From: Johan Herland @ 2010-04-21 8:46 UTC (permalink / raw) To: Junio C Hamano; +Cc: git, Johannes Sixt, Michael J Gruber On Wednesday 21 April 2010, Junio C Hamano wrote: > Johannes Sixt <j.sixt@viscovery.net> writes: > > Am 4/21/2010 9:34, schrieb Johan Herland: > >> I can confirm that this patch works with my original (more > >> complicated) scenario. I've also played around with combinations of > >> --ancestry-path, -- graph and --parents (and even --boundary), and > >> AFAICS, the new option does not clobber the other options, and (IMHO) > >> it all behaves correctly in the scenarios I've tested. > > > > How about possible interactions with --reverse; > > --date-order/--topo-order, --parents (important for gitk); > > --simplify-by-decoration (useful for your problem that triggered > > this); --full-history and --simplify-merges with path limiting? > > These are all good points. > > I am reasonably sure that parents (specifically, "rewrite_parents") is > broken. The new function should cull parents that do not appear on the > ancestry path from merges (that is what "NEEDSWORK" is about). It may or > may not break gitk, though---these off-path parents are shown as parents > of an on-path merge but will be marked as UNINTERESTING. FWIW, I added the following patch to 'gitk', and then ran it against the t6019 test repo as follows: gitk --ancestry-path D..M ...and the resulting graph is exactly what I'd expect; showing the uninteresting parents (D, G and K) as "hollow" nodes. diff --git a/gitk-git/gitk b/gitk-git/gitk index 1b0e09a..7749d2a 100644 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -190,7 +190,7 @@ proc parseviewargs {n arglist} { "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" - "--remove-empty" - "--first-parent" - "--cherry-pick" - "-S*" - "--pickaxe-all" - "--pickaxe-regex" - - "--simplify-by-decoration" { + "--simplify-by-decoration" - "--ancestry-path" { # These mean that we get a subset of the commits set filtered 1 lappend glflags $arg > I do not think reverse/date-order/topo-order would be affected by this > change, as they only change the presentation order of what limit_list() > returns; > > Also simplify-merges and full-history should be Ok, as they control what > is done in the current logic in limit_list(), which is an input to the > new logic, meaning that the new logic will work on already simplified > history. > > This is not a new problem, but I strongly suspect that cherry-pick is > broken the same way wrt "rewrite_parents". I'm not very familiar with "rewrite_parents", nor do I know exactly how it should affect/interoperate with --ancestry-path in all cases, but running git rev-list D..M -- M.t produces one commit (M), whereas git rev-list --ancestry-path D..M -- M.t produces nothing, so I suspect there is something not quite right here. ...Johan -- Johan Herland, <johan@herland.net> www.herland.net ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] revision: --ancestry-path 2010-04-21 8:46 ` [PATCH] " Johan Herland @ 2010-04-21 9:04 ` Junio C Hamano 0 siblings, 0 replies; 11+ messages in thread From: Junio C Hamano @ 2010-04-21 9:04 UTC (permalink / raw) To: Johan Herland; +Cc: git, Johannes Sixt, Michael J Gruber Johan Herland <johan@herland.net> writes: > I'm not very familiar with "rewrite_parents", nor do I know exactly how it > should affect/interoperate with --ancestry-path in all cases, but running > > git rev-list D..M -- M.t > > produces one commit (M), whereas > > git rev-list --ancestry-path D..M -- M.t > > produces nothing, so I suspect there is something not quite right here. Merge simplification gets in our way big time. D---E-------F / \ \ B---C---G---H---I---J / \ A-------K---------------L--M While traversing the history from M, we notice that M's parent is L, and then further we notice that neither the transition between J to L nor between K to L change the named path M.t, so we simplify the merge L to have K as its sole parent. This makes M appear disconnected from D's decendant chain, and causes the TMP_MARK reverse traversal not to work as intended. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] revision: --ancestry-path 2010-04-21 8:01 ` Junio C Hamano 2010-04-21 8:19 ` [PATCH v2] " Junio C Hamano 2010-04-21 8:46 ` [PATCH] " Johan Herland @ 2010-04-21 8:49 ` Junio C Hamano 2 siblings, 0 replies; 11+ messages in thread From: Junio C Hamano @ 2010-04-21 8:49 UTC (permalink / raw) To: Johannes Sixt, Johan Herland; +Cc: git, Michael J Gruber Junio C Hamano <gitster@pobox.com> writes: > I am reasonably sure that parents (specifically, "rewrite_parents") is > broken. The new function should cull parents that do not appear on the > ancestry path from merges (that is what "NEEDSWORK" is about). It may or > may not break gitk, though---these off-path parents are shown as parents > of an on-path merge but will be marked as UNINTERESTING. Thinking about it a bit more, I think this is Ok, as all the ancestors on a side branch that is off-path are marked uninteresting, and we do show uninteresting parents in the output of an interesting commit. In fact, if you do "rev-list --parents HEAD^..HEAD -- .", on a non-merge non-empty commit, you will see uninteresting HEAD^ shown as the parent of HEAD. > This is not a new problem, but I strongly suspect that cherry-pick is > broken the same way wrt "rewrite_parents". This is a different and a real issue. By marking ones that are duplicate of commits from the other side as SHOWN, we will spit out a disconnected history. It should rewrite the parent list so that children of a commit that is removed due to being a duplicate from the other side point at an ancestor of that removed commit to keep the history connected. ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2010-04-21 9:04 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-04-20 14:49 Getting 'git log' (or something else) to show me the relevant sub-graph? Johan Herland 2010-04-20 15:18 ` Michael J Gruber 2010-04-20 20:45 ` Junio C Hamano 2010-04-20 22:10 ` [PATCH] revision: --ancestry-path Junio C Hamano 2010-04-21 7:34 ` Johan Herland 2010-04-21 7:47 ` Johannes Sixt 2010-04-21 8:01 ` Junio C Hamano 2010-04-21 8:19 ` [PATCH v2] " Junio C Hamano 2010-04-21 8:46 ` [PATCH] " Johan Herland 2010-04-21 9:04 ` Junio C Hamano 2010-04-21 8:49 ` Junio C Hamano
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 NNTP newsgroup(s).