{"thread":{"id":"33794","subject":"[ANN] git-tbdiff: topic branch diff","startedAt":"2013-05-11T15:35:46Z","lastAt":"2013-05-11T19:01:07Z","messageCount":3,"participants":["Thomas Rast","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"217006","messageId":"87ip2pfs19.fsf@linux-k42r.v.cablecom.net","threadId":"33794","inReplyTo":null,"subject":"[ANN] git-tbdiff: topic branch diff","fromName":"Thomas Rast","fromEmail":"trast@inf.ethz.ch","sentAt":"2013-05-11T15:35:46Z","receivedAt":"2013-05-11T15:35:46Z","isPatch":false,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Hi,\n\nSpawned by discussion here at git-merge, I created a script that diffs\nthe state of branches.\n\nYou can grab it from\n\n  https://github.com/trast/tbdiff.git\n\nThe usage is\n\n  git tbdiff A..B C..D\n\nMake sure to pass two ranges; it doesn't check that at all, and just\nhands it off to format-patch, so that a lone commit 'foo' means\n'foo..HEAD' in the usual format-patch semantics.\n\nIt does not use a longest-common subsequence approach like all other\ndiffs, since the commits on the branch could have been reordered.\nUnlike in code, where only special cases of reorderings do not change\nthe meaning[1], these reorderings frequently do not change the semantics\nof the series.\n\nI tried an approach that goes looking for a minimum cost bipartite\nmatching, where the cost is given by the interdiff between the commits.\nThis seems to be working nicely; for example, between v2 and v3 of the\nfc/remote-hg topic[2] it shows (in glorious color that cannot be\nrepresented here):\n\n  $ git tbdiff origin/master~2..fc/remote-hg-2  origin/master..fc/remote-hg-3\n   1:  95bb1c2 =  1:  832f6a6 remote-hg: trivial test cleanups\n   2:  ebff16c =  2:  1512332 remote-hg: make sure fake bookmarks are updated\n  --: -------- >  3:  1584d8b remote-hg: fix bad state issue\n   3:  8db4546 =  4:  0d6054b remote-hg: redirect buggy mercurial output\n  --: -------- >  5:  ff9def9 remote-hg: fix bad file paths\n   5:  2406703 =  6:  f467a2d remote-hg: refactor export\n   6:  e3018d1 =  7:  70e4855 remote-hg: fix for files with spaces\n   7:  a35adfb =  8:  7af557e remote-hg: update remote bookmarks\n  --: -------- >  9:  99fe20c remote-hg: document location of stored hg repository\n   8:  4ec2382 = 10:  d360d7c remote-hg: add missing config variable in doc\n   9:  1c94a96 = 11:  0c51226 remote-hg: trivial cleanups\n   4:  fd646af = 12:  e9f3e36 remote-hg: update tags globally\n  10:  dc097ea = 13:  1ed339d remote-hg: properly report errors on bookmark pushes\n  12:  d8a548c = 14:  f887082 remote-hg: split bookmark handling\n  --: -------- > 15:  a9ac462 remote-hg: add basic author tests\n  --: -------- > 16:  c68bed9 remote-hg: add simple mail test\n  --: -------- > 17:  a350abe remote-hg: add 'insecure' option\n  11:  0870eb6 ! 18:  66e283d remote-hg: push to the appropriate branch\n      @@ -11,7 +11,7 @@\n        \n       +    # Check if the ref is supposed to be a named branch\n       +    if ref.startswith('refs/heads/branches/'):\n      -+        extra['branch'] = ref.rpartition('/')[2]\n      ++        extra['branch'] = ref.replace('refs/heads/branches/', '')\n       +\n            if mode == 'hg':\n                i = data.find('\\n--HG--\\n')\n\n  13:  a583fa6 < --: -------- remote-hg: force remote push\n  --: -------- > 19:  ff4571d remote-hg: show more proper errors\n  --: -------- > 20:  34b0b77 remote-hg: force remote push\n\nSo you can clearly see that commit old-4 moved to position 12, commit 3\nis new, etc.  The failure to match up old-13 with new-20 is not a bug;\nthe two are so different that the heuristics split it into a\ncreation/deletion.\n\nAnother use-case is when Junio picked up a series that you submitted.\nComparing Peff's jk/packed-refs-race on github with what was actually\nmerged into git.git shows\n\n  $ git tbdiff origin/next..peff/jk/packed-refs-race origin/next..7c95033\n  1:  eb4f175 ! 1:  ed485e2 resolve_ref: close race condition for packed refs\n      @@ -25,6 +25,9 @@\n       resolve_ref_unsafe. In the situation described above, we may\n       still be depending on a cached view of the packed-refs file;\n       that race will be dealt with in a future patch.\n      +\n      +Signed-off-by: Jeff King <peff@peff.net>\n      +Signed-off-by: Junio C Hamano <gitster@pobox.com>\n       diff --git a/refs.c b/refs.c\n       --- a/refs.c\n       +++ b/refs.c\n\n  2:  d9d9e0f ! 2:  40aba32 add a stat_validity struct\n      @@ -13,6 +13,9 @@\n       reuse the logic about which stat entries to trust for a\n       particular platform, but hides the complexity behind two\n       simple functions: check and update.\n      +\n      +Signed-off-by: Jeff King <peff@peff.net>\n      +Signed-off-by: Junio C Hamano <gitster@pobox.com>\n       diff --git a/cache.h b/cache.h\n       --- a/cache.h\n       +++ b/cache.h\n\n  3:  4e566d4 ! 3:  7c95033 for_each_ref: load all loose refs before packed refs\n      @@ -40,6 +40,17 @@\n       from that tip (the same thing can happen if \"repack -ad\" is\n       used, as it simply drops unreachable objects that are\n       packed).\n      +\n      +This patch solves it by loading all of the loose refs for\n      +our traversal into our in-memory cache, and then refreshing\n      +the packed-refs cache. Because a pack-refs writer will\n      +always put the new packed-refs file into place before\n      +starting the prune, we know that any loose refs we fail to\n      +see will either truly be missing, or will have already been\n      +put in the packed-refs file by the time we refresh.\n      +\n      +Signed-off-by: Jeff King <peff@peff.net>\n      +Signed-off-by: Junio C Hamano <gitster@pobox.com>\n       diff --git a/refs.c b/refs.c\n       --- a/refs.c\n       +++ b/refs.c\n      @@ -80,7 +91,7 @@\n       +\t/*\n       +\t * We must make sure that all loose refs are read before accessing the\n       +\t * packed-refs file; this avoids a race condition in which loose refs\n      -+\t * iare migrated to the packed-refs file by a simultaneous process, but\n      ++\t * are migrated to the packed-refs file by a simultaneous process, but\n       +\t * our in-memory view is from before the migration. get_packed_refs()\n       +\t * takes care of making sure our view is up to date with what is on\n       +\t * disk.\n\n  4:  9937a33 ! 4:  b6be74d get_packed_refs: reload packed-refs file when it changes\n      @@ -53,6 +53,9 @@\n       appear in their executed order to process A, by the time A\n       sees the missing loose ref, the new packed-refs file must be\n       in place.\n      +\n      +Signed-off-by: Jeff King <peff@peff.net>\n      +Signed-off-by: Junio C Hamano <gitster@pobox.com>\n       diff --git a/refs.c b/refs.c\n       --- a/refs.c\n       +++ b/refs.c\n\n\nIn this case the matching up is trivial, but you can see that it clearly\nshows the added Signoffs and edited parts of message and patch.\n\nHave fun, and let me know if it breaks!\n\n- Thomas\n\n\n[1]  in imperative languages anyway\n\n[2]  One part of that series, anyway.  The different versions make a\nreally good use-case for this tool.  I pushed four versions of that\nseries to git://github.com/trast/git.git as fc/remote-hg-[1234] so you\ncan experiment with git-tbdiff.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"217007","messageId":"87ehddfrmi.fsf@linux-k42r.v.cablecom.net","threadId":"33794","inReplyTo":"87ip2pfs19.fsf@linux-k42r.v.cablecom.net","subject":"Re: [ANN] git-tbdiff: topic branch diff","fromName":"Thomas Rast","fromEmail":"trast@inf.ethz.ch","sentAt":"2013-05-11T15:44:37Z","receivedAt":"2013-05-11T15:44:37Z","isPatch":false,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Thomas Rast <trast@inf.ethz.ch> writes:\n\n> Hi,\n>\n> Spawned by discussion here at git-merge, I created a script that diffs\n> the state of branches.\n>\n> You can grab it from\n>\n>   https://github.com/trast/tbdiff.git\n>\n> The usage is\n>\n>   git tbdiff A..B C..D\n\nBTW, I should've mentioned this here too (it's in the README shown on\ngithub): it currently depends on the 'hungarian' module for the matching\nalgorithm, found here:\n\n  https://pypi.python.org/pypi/hungarian\n\nIt's easy to install, but in the interests of wider usability I'll\nprobably write the same in pure python eventually.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"217034","messageId":"7vsj1twdcc.fsf@alter.siamese.dyndns.org","threadId":"33794","inReplyTo":"87ip2pfs19.fsf@linux-k42r.v.cablecom.net","subject":"Re: [ANN] git-tbdiff: topic branch diff","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-05-11T19:01:07Z","receivedAt":"2013-05-11T19:01:07Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@inf.ethz.ch> writes:\n\n> In this case the matching up is trivial, but you can see that it clearly\n> shows the added Signoffs and edited parts of message and patch.\n>\n> Have fun, and let me know if it breaks!\n\nNice.\n\nI need to do this kind of comparison quite often, when a rerolled\nseries is received.  Since there is no reason to re_base_ the series\nwith with an updated one on a different fork-point, I tend to do:\n\n\t$ git checkout master...tr/topic\n        $ git am -s3c ./+tr-topic-2.mbox\n\t... inspect the differences between tr/topic and HEAD ...\n        $ git branch -f tr/topic\n\nwhere the first step finds the bottom boundary of the previous\niteration and detaches the HEAD at the commit, the second step\napplies the rerolled series and the topic branch is updated in\nthe final step.\n\nThe \"inspect the differences\" step can be helped if I can use this\ntool to say\n\n\t$ git tbdiff master..tr/topic master..HEAD\n\nIt would probably be trivial to extend the command line parser to\nalso accept\n\n\t$ git tbdiff tr/topic...\n\nbut that is an icing on the cake.\n"}]}