From 59c0ed976def8772ce16e1d4d67461b2251f4ed2 Mon Sep 17 00:00:00 2001 From: Henry Gabryjelski <740168+henrygab@users.noreply.github.com> Date: Sat, 13 Jun 2026 15:43:54 -0700 Subject: [PATCH] Improve git blame by ignoring certain commits --- .git-blame-ignore-revs | 377 +++++++++++++++++++++++++++++++ filter_git_blame.md | 490 +++++++++++++++++++++++++++++++++++++++++ filter_git_blame.sh | 32 +++ 3 files changed, 899 insertions(+) create mode 100644 .git-blame-ignore-revs create mode 100644 filter_git_blame.md create mode 100755 filter_git_blame.sh diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..8f7c4bc56 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,377 @@ +0033715a595d34e1e603a144c8af35c21587a9fb +00fb88ee4c85ed2268eb9705bb8ec519e125cb5b +011f973e7ff145ee0f6ccf58c850765513c43d04 +01632eb9b0ade135173320b649f89cf231e9206c +01b9197353107377c9c788b7c034dd123383f33b +0204714f7ed54adb0c5a0fec4ff3a18a6af71444 +0254b2a63ad4a9f2855f0d85aa7bedfe72582b38 +025c98fc53c1433f2babe6a79a1542eca695c33f +0373696662b0372bf1e1512b8c788990fc0bbd4c +048291dcb3ea6580699ff48b8d5614cbeb4ec3b0 +04bf1b8db7666d18dc959104600c2e97882cc789 +05576ba06b8c595f1c44b8903caaef782aaccacc +05c5179b41d66fe9276339dfc73d5885d6c3f6fc +0673dd206eb9cd997e71826dbb1536d34ea3fb0e +09bb3f62a1d1d423bdf98c5fa56eb3f22c9c3047 +0a19c3e8917f9fa8d157b60afa7a27cf0e784af5 +0b4d584dd7b276b9efcb7b613023b7208bc48787 +0bad12fcac8e9f8bedfe32ff28c2176c3c8e8857 +0c810ff4fdc3e9acf5b582d0ec305f83e9ab6294 +0cad19b462c3b45523b8107d2d1e506e6225b0cf +0d38da5de2ad62ad8ac9961bb77da18c37df84bd +0db962640c6bb0033af547305ee29ed3ead33d70 +0dc6f38741b6220ce5a119df5548539b8dcdd4ff +0eae1e25637871b7163deaa256b65533fe554d25 +0fbc101ac51f5d4e292a93a2d378a65f8c2c7002 +0fbf7d6328ed2088c72564f55bfa3e9b56cc75e8 +0fcd79617d4cec8109377d0194d7e038f43c2fca +0fee0c97cb7526f58f279e43a91dc0a06e450738 +10243d9bce05782ca8300c7d5ea361657295e06b +10362d68cfb2cf3069939075b77af41d70375987 +10e28291236f3de530ad12c06874bbef1ecd3492 +11a95d73510ddee705e85a0c64f017f186bfd35d +11afbdfba31ee7f716965a85de5eee16e6b3f289 +127aebd8b0e540d0b136b6e858c9abdca52356a1 +12a28fcd16e5934b8d7bed94ecebf3bbc2ad0121 +12c0eb97fb111def689e761d53beef33f94c277b +140a7f34a443053e31156a36403e5beaa7f438c5 +149236d8b3c20d202cfe042b807289fdd0429abd +155444440eb134bdb70127a6a022819aca1528ac +15597c6c38e02ec9a456a22487b9e2f84d771a69 +167cca5fc0d8145d476543ae598cfe02d90d9015 +16f51079155218a61434d05b52fc5b25dad9dbf2 +17d7fb8abb600ae6e7a7675efe7892726f11fe38 +182f239d21390e6c66b071e1637b0f3593972d09 +19a5a70cc89d7f0c5da040f87a3fd2e1fc1445dc +1a19640d188c34babac184a00946bfece80a3ff4 +1a788b1c14ec05a1c5f81a79f9b72ce933cedd06 +1abcae291cfaace63daca69ca0c138c578614c50 +1afb335478ad34275fe9a6d566d5d9e70b68d240 +1b16b955653139cfad1e5a97e32d5d2af68da78c +1b9490c6aa8125c591b75c015c7efd84e9ae8de0 +1c9405605766fa8b07a499afd052a4ae41a9c7cd +1e54cd661c9005ad8a6eaf6362a61fe11026df8d +1f27be076bb8d0fd2651596fc53c514b65cb713e +1f470483ec473e88424c7e6540b61056bb143e86 +1f9ec97dfd5c12bdf43b8d970b10d6cf6a5fb959 +1fbb838d3aa4bda965ebc4da0e08d17b61ba7bd7 +2022df10680cd245ae27a711402e8571679b373a +20547d773b8e1c940fcfd34d35ea4f13edf1048e +20696fb44a408abe31e852129af3a4777632f886 +2137e2b6c871ebaae3029188db091cf5f278a8dd +2184b0dde814c5661abcda28be9bcea097b11efa +221a06321b8b3009e38e6409073e93d0f338f859 +224cb2ffd7432824cec132a97fa02fc9106e4f92 +224e2f68b21947e1fd3e4468233587b246a38c0f +225d406d21bb1908d487b4f61cf80a8c6a124f24 +23b4a47e94d0578cb6ddb2d24dbeb050cfd6d625 +2441b24124c0c0fa8fef4575c5325a589ac67e56 +24665367ea0c5fdc44af60a39ac8c4732374fe41 +2517e8b345f8591e38eb4a6221876c0dc401578c +28dc6c717bf0c3afb051e007cb27c5f927267d28 +294d2aa96b2c76107875419a1f540287bbc63c9d +295286e68aec9327a3580417009ec506abdf651a +29f1147aadc64bf9bbcaf9bafcb856da4ece1e68 +2a705d3a44c8e0d24da0c3a71940711ce18e0014 +2bf2bb301a4554b142538c9691073a6effa9144e +2c0a8bbd91dd0257c47f3de22a8a84e59a7b6d97 +2c73337bde37e2271534b621d454c85e2fe6e364 +2cfe4d33c5a9091611c1c9476d28c9b32f1ce9f0 +2d2b12875492cfd8fa3c5d3fa43bb6b78a5ee906 +2db34e0ad6936ec53f28fcad1cd44a5b14519460 +2eaa60e95e38ac6a6fc3b9c9e30ad288df3df764 +2f85bced17dee80fbe0b3cb0e8b481388c570693 +30ae0bf521fb14d76d56b72682a926ea502d1a6c +31125b9d342f0143ada6428e011334ef2da64728 +31df889f97ff93de7cae20079dc545d0aca3fd36 +3235468e05d1fc6c7856071e363f3341388f80ef +3275decfb091b6ed92a41df1729733eaee7e27c7 +33a17fcd6706e6e27cf7f1b15984d55996762b73 +346af6e3d1f992fb96491cd2bf7e8add340cb3b0 +3491157345763053a6879729ccbe2e9ce26982c8 +35bea0fbaaef34102566b24fe797ca72c21107c1 +370c4cf5a43f5a5dd3afd15d0162c8442e8bfc96 +372a6958fa41cdd8607032b502cc3f61541d4116 +3771c150f0345b85e1109197306bf2269db9c1b6 +3936afcca99f716cc63928fbebd0271a51ed3046 +395d0f9ebf7c5f7275e5423f802f35b501d50f2e +39fd06f947a5b949f8e8f1d151965e2e0714d298 +3ad822844f4a0f83de26cfb67808fc13d795cabd +3ae9070dd83a1f5460f2c4857325847b6d3b9891 +3baf5955781de0b3d58e08efa97feafa6050f690 +3c07e264c95daf0b4e744aa14fdf1e4a2395cfce +3c31ee793dba08d7fe93aae3269ea0a0bd9d1037 +3c47e78597beb24d2840527acd1e110d4fbe3db4 +3c51ee4547e65e1c7ceaa8d7f87f47b644d0e2d2 +3cd282ce873383ecd29b00f96cfce2ba81c04de6 +3ee657c0f70ce1c390c7540febe9cb23991ffd57 +3f370438e5b9ed4ad5abee230e9f1efc7a43380e +3f6c914b9917aab460c3417932ee98a00ecc1d01 +3f71be870b7c9c356ea072f5e9362a07524bddab +3f9ddf9b2978e2f5ce7c87b7749b88cbe170831f +3fd8a640c1f45f3bd4a676222015a39c5e331e3f +3fead5b93c641e61ca5336e39566c7c325cad789 +404161d8b0b2dccdeccaf4239dbef89f8ddafda1 +41f36ca20b339246848e512a039c2cf18943bed8 +422e0187185fc976a9e41c8bc746cada37e46849 +4320486a3bf7250a77edb7762662aa55fbc51fdc +43e1c5f22deb0b1e1f337fbb81f2f2bf46bc5703 +45a1a8375242bd7992357f192ed4b478c78f16f0 +4620c0b21ef3d17684de5d14ba9569355d43bf82 +46767a07e7bc365d2127d392d8eff1a4876a7fab +47b60d84de4d9211f0a0ae0a25deb35147c5fbe9 +480bd87207c18868e1627c589e665343f5d57d7b +4814dc5bfb38c6c46163b7adee7d371ec7925397 +4896390dde5b194cfe4a343ca144493c6dd006bd +4919d14c13d9d07fbca55a40ca034350bfa82c38 +4a282b32c2d34552a9fb235ca8bf30f3b2af392d +4ab382065a06a1a0750130e4c6945f94fe9584f5 +4b0096c13520ca01548a89bfe3658749a0309eea +4b64a45899cb5dc0eea5840b214f70436ee4d7fc +4c00cc5bcfd02818c7ca383e546ae3e65be09689 +4c44138557112513e836ff65f5da6273548e661a +4e48b10ab9777a833800b5e653af45c5fb8624ae +4eb5907637e22924967f212f79ba23ffa053fa2f +4ed57c7c4d2879797df68f28424eafbdcea666a5 +50429c8476fefdf4e368bf256846663ec57e5960 +506d660c293001d4eddd2263929253110071df1d +50b1591d0f46c67cd4960f4c43d0e2135d16dd6e +517f0886ba66963225b9070fff2dd9a545a25738 +51ad5c23321b2f1a22f4b6f371f9a8e438dcbcac +51fb73f4c8dc70233d206204cf119bb96236025f +536ddd17fde580f98bb30c277d87653e77ac7acc +53ecdaf78f9fab0654df3b656a9889f296a9607a +548c2ed9989377799279e3198968f98138de7803 +5561466fe8b3a649d8a5567b1f2ddf51a3aab7a2 +55fa5e7c35986adf3f1cd9c5f0ffbac7bd44ddbf +56b4a480cdb4efd6b63e242f3f234b0aee9fe4f4 +56d25b0786527f92cea417423c0fc1d46587ebdc +5768ae9395395def993b1f6436e72ac2bed817ab +58a963d41f5ed25516002d8face2a34ff9d93287 +59cd54e17800f10993c1285205b154f6d866c6e7 +5a4584358c11cbe7342ba158a17d8ecb62a6701a +5ac42add1a7a590e028d1bfa30de1f0dad290cc8 +5acd681f8f72ced055d4dc5f129dbfdcacb94427 +5b1e16b14eb0d883433ae54e611de70c4b30d80d +5b51f4b4ee948fb85a52d5d16881be0a15de1f61 +5c1b99b0487ecfb94a321566b1febd09e526f9b9 +5d5d9d9be0b86d5343378948d5c9c89df9320a7f +5ddfedbad020fc4e8aad59876a2d60220cb67631 +5de11784660df530bbe260007c0a23ff7ecc25d1 +5f8cd5caca3cb1e8006c5aaccf8091a9b24c1bed +60719c77889ee53217304117ada1e3d61149976d +608d38c4b58daa1e4b6d51bc6c02ca182a323ca8 +60b0742941eaf7ffe6c2ea0b28ff7deedffca25c +6290d505648f8d6bde4be9e1d0e77e941a11c006 +62a9a46cc75814ee906548e347bd9ec50cd7082f +62a9f5dcc2029c2be9ce8cb9fc625bcf5a95a618 +638d6984456a7c9c9dbafd0a3dc53df5f4b0a188 +64ce30c06c8a4c24cb241b6cdacbf64ec9829d74 +6543a498b032c0db9428503bedf2bc2bb2ca6a34 +662d3259ca128e0837d6f296b8daaba82e88701f +66332149571dc7bb3f10d5eeaa65d1009ac42d0d +673f087db9b22084b70af6c98e4c48269c5fd351 +67fc919c72b3fbb96b283893a374f9db5914fb44 +68d79527e51b4ec34ce82ce2919de23a3fb4d748 +6a5a5f7397e11b940f2f249e9bdb26224fcb6e3e +6bb301f4dac7a47e44b6396b4747bd4da8333bb4 +6cadbbc3e74a35a88552659d00becf1d1a21a5ae +6dbde61c4e625b3cd1cb9164ef8b3649f97e2b3d +6e0673fd02ad381c457f3dc5aeb5969ee7ccbde4 +6eb02c5f8e12d37dac5ee26135c6392b6618fa35 +6f1ed5a9bf90466534dfa12e8c6702f332bfb979 +6f20f86c2d7eb2f6900172dac6d678bfbff061b6 +71f96ba1e7c9c672563646309fba34252d981b2c +72f26825448eaa5a3935efeb2d7f4435642cc184 +72ff81c80f7a2a803554640f49487e80e63e38c8 +73f62ae27eb9b3b1d4f25cc54b92985b539b1c6b +76f2c2c95e8d8551bc0df3232cc46fa283b4875c +7750e60ac39193abfaa0d51a7028eade1c8ed107 +777cb5a8ce7d7d68e8a660dff83fb97511e99f84 +785d790ea0d3e0cd2b4a10f09735e8cd41d3d715 +78cc7e10a586d38bce09c0ac01d434381582c03f +7910464a8537714cec4393c10a35ccc5954d2f4f +7a867cadcd992f9186d5178310c3a572f99977c5 +7b34b210b4ba6f0dd6d16f7c9fb2e278a25896aa +7c7dfa7d392fde020cebd2380eb3961f62f6a794 +7c8be44dd68630c62a7cf5848a798a947ed58e39 +7cd509c77c30058d8a6e180c4b30a3f237996755 +7ce190593c54a0ed253e469aff6815d38808b0ef +7ce86a0de95592bcf30451e2020bc99599d6d3c8 +7dc65bf0f7806e6272dce2ab76a80851def76fcd +7eac4818abd4d3811bcb21d39d3ab4b980e2aca0 +7ed8f08e096e94a86e1e8022bd6d10d07485f7a1 +7efd02b6e0cec3c8954d67495bdf42dc8212aed0 +8029e081535672dae582c7be6a8349cce0aa9c0e +805b8e1e448c15ce33b464859598879b1d5de4ad +8172c708c3b34ba79b5c96d56e08ad21c466aec8 +81c6918ac582d1cd9643404b9e545b5e214eb7a1 +827439bddfc9f99ee6b10b40ac634402c5121eaa +8338cd850c3db6b14e43c51f8f209a39e69c5328 +8472c6ffe7c3a8450fc84e945018851fc7f9cd32 +84bc13884135ddc6d142cc7858cbe40d191f2ea4 +857f0c0d9bce056acbca828155f93a3faa712dec +866df868258c2f427493a7f81a4fe23f3281c291 +869ae27f35bd7404725b39a7cb27df5f0dabdb4d +8760068a0304940654f75e54b8ff2895f15fa6a5 +8772edb6b85a886064e08ea555a08b8a9d7923fe +8792679fc8126abf19fcb900542562a5a8cb46d8 +88237e60c138aa7eb54c8601ff8f6b556f3009aa +886104e4cb151a0f1bccd626e893ce5bb5801cf2 +89a05b47a80d261597d7320d0b292a0989243cd5 +89becb8d9f13b463129728a0dc511dfcb720a835 +89f43c12216be38534ffbebc61c995d56b2ccc17 +8af75cb220ac5415cdd600aab369553804058cdd +8d4acbe3a622d642c65547243e7809d9d1315955 +8d9dd8b346f742c673988b7207721057ab3fe2e2 +8dd963d305a31429bba4ae1418b65142fa39cd24 +8e6258936caea0fda3c6b82d2038cfae66acac66 +90a9c215de47d48c4dbf867149f1a06a63f6d017 +90fb78ff1b5bbbd0559ff45fcb642a8b0be52790 +910c4d8c92b85ca0bb6ce70499b86fddba44f0c4 +928c0e41626a443b7e70e03a973a9dbafebbbec1 +9395f8c8ac1a6949a7f241c9a04a23a3264868b1 +94192d0976c7e3e23daa14a7172344855a6f8e2c +945fb82b48054d4096f82fda892abd21422a0025 +951d6cd218f26cf0c65ee9dfa2b19befd3724842 +971c0126de3dcac76cc90d01fbcd52c186d58f4c +977b198816685059c99989bb57b2282229da2678 +9880df8ca7a41bfaa5e2b31ab40c3e9aa72519f7 +9883343cccde13da9e72adee937bcaa7f8541d16 +98d940874625b246803d790985520afccb377ec2 +997567a9d68fa9227a25739c52f992988335f4af +99dcb8122ac77c6c3566b425e46499ecca5cc4e7 +9a746db6f954aee0ad90675311b5f245b3a0ef10 +9b44590f8dd2e52d735031f3784f75a59c01fd5e +9beabaabf528f5e39e399e86d9b9f4610135d268 +9ce99862e89d38af219583a044ed65bad7652a60 +9da0073e0dd9758cece4bf215654c4550929c971 +9f36ce1b9d67065150c790e21b37d0df191e2833 +9f4620c92c57af2222a916db376b4a17745d1819 +9fa8d9175b84dea682eee9f135ade182075d66b1 +9fac23567be04ffceaaa61891fff530e2b40e0fa +a0e110941a502bc2135f53b1883c655ba117c331 +a24c7334cfe84fd1651b0810ebb48ffba1fbddb0 +a27b3af4cbcc634d49b396a23f8f08688a9a49f0 +a40c2727e6c3797f3edf843737d228e0b752c2ba +a4bc2c552134705370bf7ce1b09040e6babc6ed3 +a55e597e362d966789575f11e6dc8dce0936e6c7 +a592b349cb69202eb4838c5bb2fff9a428e9ee31 +a5f42128099ce68bf2d765f31edcf71baeb1476e +a68b8983f25260acd01c9cda44a3d39cf288e1cd +a769752aafffd3cb0073fc2c559e68d8a8ccc88b +a7ececcb5dfe631fb159c17bcddf574aeb4123ca +a7f3a42aff1a65d8a101a31d5d26e3818bec169e +a8aadb27515819c52c6068a29340566616d59a4d +a8ccba77d63d1576bfac054eba1425e2379b28a3 +a9aaa3040ec12900b86294b4273645771b1f0a6e +a9b265ff1b9e3c69d14659b27117ffeaf6b2d3f2 +ab249d0d76a11a23aab65a818a9e42671eefda74 +abacf673dced8638c3a39f02a7f6b126dcd9414a +ac233a346a70a55832dfbe3365769b66eac6f486 +ac8e74c1d9de2e74f0ca87b6484ad650cba12837 +b03f9402c12f76ad7d7e2f09d185d6eb1c2e4271 +b0b51e4222eb73050beac7ded715c256c1aa035f +b0dbbd3683afeafcf45bfb79c9a2c0a01f88d14d +b2996aa69a8dd85a2d15598b6d7006746825804b +b34851d2812fd5712273ae6ca5e2a25ebb6d8cd8 +b3731c60030e3266598eb02f4c56ca011538a30f +b3f84bf7b3459152e3bfef8e9c9928ff7ccc2236 +b538a67ba348f339df71e76fbf53997db21a7477 +b55a33b1d7b2950fbe7e42d3b308ef21a06183c3 +b55c186b3a52cbfe542a787243d3e8d3a1bbf49f +b57f40e3d78ef067ab6c592cab69255c1db6e86e +b63495cce72d3bd192a64358d6db01de4f6f8002 +b8458322e15deb33f143ec170427a380434bbf75 +b86d9f2b969fe44121f7f58929120b1dfa2a84b2 +b910d7ea94d6f53fa99afed7c72fe29f83c6d63c +b9300e94ce454017f992422b924e8768fd12b917 +b9e0b57cd89acc3b4ba5e9fde18aac5084bc8bf9 +ba6ef2a1bcb47ddd59accf70cbae9ce04cb0e128 +ba8e90edfd49c144d814e2664f437a54b252f46c +bb6ea597b4ba00dffa99c67e613db6525fc4f6e0 +bd8824a2f308a597690c91f47efb279801534870 +beb6a8bf110e5323a40684b195e459f323c525e2 +bf597dd2d22d678782da0c49e5bfc27dfca188a6 +bf8cd5c00b02e6bc9b82f0fb451e2798da6b76a0 +c0f4abb8d474b6e5778996f17380d9c05de1a9b3 +c193eef3261d00d58a07a3f3169b31e583713198 +c2092ee2ed6331b3ecc363cd56b4697af9f6e081 +c2eb6395e242986c10155e1917614e6c1dfce112 +c3024fe9f256e27b4238a4e016a879db29f6b657 +c349aa0a13174a8989a31d1749c2c055062d6e2d +c3b7f6ca5816e9b3bca9eca2729b6017369524ae +c3c59e35cbf6687edd548c383ae8c60495f96731 +c46119d7553d1366ada9b875a259ff057b53a4a9 +c4bb26b1ef9a4c2961d42b6de927dd8410ef4f32 +c50c0f1630bc104717c12556f89fffe2d55e33cb +c5658f2a0b4f0da5028833c214a070368289914f +c7d2ccb8bd4177e240d836759b64a2802641847f +c83ff9da0f7975b6c682d1b32a8942255ffbf405 +c9984985fdebafcf2975437e18dfe8132f52f450 +ca40ee99d6729a10240156786e57ca9bda96917d +cb5aacd33aee7a22f4e82f2f7274a4e147699892 +cc6f497897b762ba8ef96fcdd7b37218414f9f89 +cd19bf81a8fc1d8d88e938f87f799cbb0a0d829e +cd36770c5ef730598ee184fa8c1e158bfe2cf770 +cd825bfac89a878d6717d5a326bd1377d93d067c +cdb99fe65096ef874da44c00c8f9112148ab9168 +cf63b9e022bb36ea4237e8b4acdbd128b85cffaf +d2a2acf69ce8a73813161f5d237eeb5d0672d4ea +d2c290c7c38276c3760bd93d61186ff36a8e7ad2 +d3248a7059bbb313e6548c79b5420cb1059cad7f +d37547917ea08977174c14fd4367a94f51f5c118 +d39b113b4075ee8afe78d228a8c130cedc071626 +d3c6c430a803156a494d590140727d8aa16e3d3d +d43f6ffc441adbb7271f6e05f598c48b199bf786 +d46d23d079dbcf4cd617722e1d7ceff2c540b4e0 +d793a601da396d2ac9f3cd186319ef562b713c87 +d9195effc7a9e03bb34f55145acd325084534c0a +d94c74b24b4c680b778c8af3af9410d3ba7cb077 +db083034d612831024bc26c76c6f24a3afd37955 +db4aa08b78205580474bab5d755ac414ed2c7adb +dbec6edfa7e411e09b26946ac52e6b7e37225e85 +dd5679a53a3454a5af080554aa2194662fba46ea +ddaa88406414ebdee6dc74d6f99e7177d780bdea +deb48d231110d4f40141473e6e0711c9080ef9f3 +dec92755b6bc99fabc448aa346dbecb5e25fd864 +dfcb83a15407617d73ba191f171bdbe0ef8c78d7 +e2f59d215fed5dbba2e68e21e6c14120587a305f +e460a4e6ca6fe054ff9a2b58e65ad58679566e68 +e50c2f00ccda2e28c1f9d6bf5ef18bd79ea520fa +e550f8ccc85b745e3961a096b5e3a602adaa5034 +e5db57747c1568ad44788a2673c82f52192957c9 +e822cf5b1e7f9b76bbbb32df5e4d8f9bfbf06a2a +ea12d31a314122a447625e6f706dfa4cb8727cf1 +ea7f2e03d38bd24b862392af04014646b9ca5942 +eafeb4712655182434a6b0e831f0a8c0b2489ed5 +eb23028514e301d878358bf11ce31210517888fc +eb3c48cd259c27cf2df20f8afb30703eb2f549cd +eb635a2efc9c12f515f946ff9f7c82883b1e9017 +ebba11b3f551cbca47ac3d88a249bc1b865dcf5c +ecb535cc58b8ba01301c07c0168f3179e2eca2da +ed10409bfd7114ec6ec99c8a68372f9eb64c386e +ed11e4a093b61b58c20d823bec86bca18b44871c +eef2a6308138b08bbf04f529957dd9da8b0fb6a8 +ef7c30121715a4123101d8239458748e525946d9 +f13149826c22b7917da90e3aa66429d87a0f6203 +f1df27e6bbecb0c7728129115463006a315d9e77 +f2bc066858b22dfa397d2533dea1e724793dd220 +f3f3a5a270c637ce90860335d1f6372e609a60b4 +f40600f997f9b259f6944236ad7dbc9abae2ace9 +f416fa9b4f933b22cbe15f904975b487fa6cd864 +f428b016c12e4cec979a228beeea29175bd6f5c6 +f5aa78e668913218a98131577bd2f658ca9efc76 +f61d19276ba17091742c7a56c70e6eb2c26af007 +f63cf021788ab390594374e5cc7463df24cc8e03 +f69739da7918752a8db82fd013c7d52cf43d6b4b +f6e013c7fc5a28166623496c2a179ac57f16b2c4 +f761874347a864421c66db0372e4b7e945f34782 +fa10e7464eb7f7ac75320228c3cd90ef25bb182f +fb9f7d6562a1632f8c0ffb2d504a61b0a11c446a +fd39493cac591d0263db8a805c952e1f5ab60564 +fd4edc390f537c82110f7169b6bdf60f77a9571d +fd7a25beedbb4870a90f87f45362491ecfd957e8 +fea3185922d51a3432938010ad269ecf5aa466f7 +ffb596373f11fd1a58aded452a26a666c552003d +ffdd338dea860b765fb5fe26bb996c9adc81f911 diff --git a/filter_git_blame.md b/filter_git_blame.md new file mode 100644 index 000000000..eb73471d6 --- /dev/null +++ b/filter_git_blame.md @@ -0,0 +1,490 @@ +# Fixing git blame + +If this isn't working for you, ensure you've configured git to use the generated file: +``` +git config blame.ignoreRevsFile .git-blame-ignore-revs +``` + +To update the checked-in data: +``` +./filter_git_blame.sh +``` + + + + +## Genesis of the shell script + +It all started out seeming so easy. + +This is the messy story, without pulling punches, showing how I +got from a simple one-liner, to the final script now setup +for filtering git blame results. + +It all started with a dream of simply doing: + +``` +git log --grep="^make style$" --grep="^style$" --grep="^cppcheck" --format="%H" | sort -u > .git-blame-ignore-revs +``` + +But this wasn't matching the first two patterns against the entire commit message, +it was matching against ANY of the (potentially many) lines in the commit message. + +## Fixup how to get the commits of interest.... + +My goal was to match only when the **entire** commit message begans with the pattern. +For some patterns, I wanted to match against the entire multi-line message. + +This seemed like something designed for `awk`, and `git log --format=...` allows +inserting null characters, so I started throwing things together. +I started with the format message `'%H%x00%B'` ... the multi-line commit message, null byte, the commit hash. + +For `awk`, `RS='\0'` make the null character the record separator. Thus began +my journey into madness. + +### Attempt #1 ... + +#### Verify the output format + +``` +clear; git log --format='%H%x00%B' -n 3 +``` + +Looks good. + +#### Verify AWK is parsing one commit + hash + +``` +clear; git log --format='%H%x00%B' -n 3 | awk -v RS='\0' '{ print "\nvvvvvvvvvvvvvvvv\n" $0 "\n^^^^^^^^^^^^^^^^\n" }' +``` + +This is NO GOOD. The first hash is considered a record by itself. +Then the first message + second hash are taken together. + +### Attempt #2 ... + +Let's try to have each record be of the form: + +`([0-9a-fA-F]{40})\n([^\x00]*)\x00`, which means that +`$1` == commit hash, and +`$2` == message body. + +#### Verify the output format + +``` +clear; git log --format='%H%n%B%x00' -n 3 +``` + +Looking good. + +#### Verify AWK is parsing one commit + hash + +``` +clear; git log --format='%H%n%B%x00' -n 3 | awk -v RS='\0' '{ print "\nvvvvvvvvvvvvvvvv\n" $0 "\n^^^^^^^^^^^^^^^^\n" }' +``` + +This is OK, although the hashes in second and later records have a prepended newline. +That's easy enough to workaround. + + +#### Verify AWK is splitting $1 as commit hash and $2 as message + +``` +clear; git log --format='%H%n%B%x00' -n 3 | awk -v RS='\0' '{ print "\nvvvvvvvvvvvvvvvv\n" $0 "\n--------\n" $1 "\n--------\n" $2 "\n^^^^^^^^^^^^^^^^\n" }' +``` + +And... failure. Apparently, AWK split the records on the null +character, but continues to split the fields with `[ \t\n]+` (default field separator). + +### Attempt #3 ... + +Maybe change each record to be of the form: + +`([0-9a-fA-F]{39})\x00([^\x00]*)\x00`, which means that +`$1` == commit hash, and +`$2` == message body. + +Then, maybe we can make the field separator be the null character. + +#### Verify the output format + +``` +clear; git log --format='%H%x00%B%x00' -n 3 +``` + +Looks good. + + +#### Verify AWK is parsing one commit + hash + +``` +clear; git log --format='%H%x00%B%x00' -n 3 | awk -v RS='\0' '{ print "\nvvvvvvvvvvvvvvvv\n" $0 "\n^^^^^^^^^^^^^^^^\n" }' +``` + +This is problematic ... it's only getting EITHER the commit hash OR the commit message. +Of course, this makes sense after I thought about it. The records separator was the +null byte, and I'd effectively split one record into two. + +This is likely workable, by parsing all the lines, then matching them up. +Defer that ... it seems too much work. There might be an easier way. + +#### Verify AWK is splitting $1 as commit hash and $2 as message + +... moot ... + +### Attempt #4 ... + +Let's return to having each record be of the form: + +`([0-9a-fA-F]{39})\n([^\x00]*)\x00`, which means that +`$1` == commit hash, and +`$2` == message body. + +We can probably rely on the commit hash being a hex string, +and then parse the rest. + + +#### Verify the output format + +``` +clear; git log --format='%H%n%B%x00' -n 3 +``` + +Looks good.... + +#### Verify AWK is parsing one commit + hash + +``` +clear; git log --format='%H%n%B%x00' -n 3 | awk -v RS='\0' '{ print "\nvvvvvvvvvvvvvvvv\n" $0 "\n^^^^^^^^^^^^^^^^\n" }' +``` + +Note that records after the first have an additional newline, prior to the hash, +and `$0` is the entire record, in the form: + +`^\n?([0-9A-Fa-f]{40})\n([^\x00]*)$` + +This will put the entire message (including multi-line) into match group 2, +while the hash is in match group 1. + +#### Verify AWK is splitting $1 as commit hash and $2 as message + +We cannot just change FS to `\n`, because `awk` doesn't have a simple way +to say, "all the fields after $1", and we want all lines of the multiline +commit messages. + +So, first split matches[1] = commit hash, matches[2] = full message +(including multiline) using match. Verify this. + +NOTE: This is effectively multiline matching because `RS` is no longer `\n`. + +``` +clear; git log --format='%H%n%B%x00' -n 3 | awk -v RS='\0' '{ + if (match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) { + print "\nvvvvvvvvvvvvvvvv\n" matches[0] "\n--------\n" matches[1] "\n--------\n" matches[2] "\n^^^^^^^^^^^^^^^^\n" + } +}' +``` + +Looks promising.... + +#### Limit output count at matching stage. + +Thus far, I was limiting the output by limiting the count at `git log`. +Instead, limit the output within the `awk` script itself. + +NOTE: This is effectively multiline matching because `RS` is no longer `\n`. + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' '{ + if (match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) { + print "\nvvvvvvvvvvvvvvvv\n" matches[0] "\n--------\n" matches[1] "\n--------\n" matches[2] "\n^^^^^^^^^^^^^^^^\n" + if (++count == 3) exit + } +}' +``` + +So far, so good.... + + +#### Filter to only the interesting messages + +Can try this directly in first match, but it's messy and hard to maintain, +and also hard to validate that each match type works. + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' '{ + if (match($0, /^\n?([0-9A-Fa-f]{40})\n((make style(\r?\n)?$)|(style(\r?\n)?$)|(cppcheck[.\r\n]*$))/, matches)) { + print "\nvvvvvvvvvvvvvvvv\n" matches[0] "\n--------\n" matches[1] "\n--------\n" matches[2] "\n^^^^^^^^^^^^^^^^\n" + if (++count == 10) exit + } +}' +``` + +While there was output, it did not provide any confidence. +Need to try something different... + +#### Easier to maintain filtering + +After first use of `match()` to split the commit from the message, +remove any trailing `\n` and `\r\n` from the end of the message. + +Compare the matches individually, and have `0` lines just +to make it easy to track additions/removals/changes in source +control (which is typically line-based). + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' '{ + if (match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) { + msg = matches[2] + sub(/( *(\r?\n))+$/, "", msg) + if (0 || + msg ~ /^make style$/ || + msg ~ /^make miscchecks$/ || + msg ~ /^cppcheck/ || + 0) { + print "\nvvvvvvvvvvvvvvvv\n" matches[0] "\n--------\n" matches[1] "\n--------\n`" msg "`\n^^^^^^^^^^^^^^^^\n" + if (++count == 3) exit + } + } +}' +``` + +Meh. Able to comment out lines to test, but still +feels blargh. + +#### Easier to validate filtering + +Let's match individually, and limit each match type to +only print four times. This should allow easily reviewing +the output. + + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' '{ + if (match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) { + do_print = 0 + msg = matches[2] + sub(/( *(\r?\n))+$/, "", msg) + if (msg ~ /^make style$/) { + if (++cnt_make_style < 4) { + do_print = 1 + } + } else if (msg ~ /^make miscchecks$/) { + if (++cnt_make_miscchecks < 4) { + do_print = 1 + } + } else if (msg ~ /^cppcheck/){ + if (++cnt_cppcheck < 4) { + do_print = 1 + } + } + if (do_print) { + print "\nvvvvvvvvvvvvvvvv\n" matches[0] "\n--------\n" matches[1] "\n--------\n`" msg "`\n^^^^^^^^^^^^^^^^\n" + } + } +}' +``` + +OK, that worked. Still want something better.... + + +#### Make it table-based? + +`awk` has both `BEGIN` and `END` blocks. Use the `BEGIN` +block to setup a table of patterns to be matched, along +with a limit for how many of those matches to dump to screen. +Use the `END` block to provide a summary of how many lines matched + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' ' + BEGIN { + # Table for easier testing + # Set limits[i] to 0 to suppress printing altogether, else count of matches that print + # Set found[i] to 0 + # Set patterns[i] to the regex-like match pattern + i = 0 + limits[i] = 0; found[i] = 0; patterns[i++] = "^make style$" + limits[i] = 0; found[i] = 0; patterns[i++] = "^make miscchecks$" + limits[i] = 0; found[i] = 0; patterns[i++] = "^cppcheck" + total_pattern_count = i + total_records = 0 + total_records_compared_against_patterns = 0 + total_records_matched_patterns = 0 + } + { + ++total_records + if (!match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) { + # Turns out only whitespace ... so false positive? + # print "FAIL: \n------\n" $0 "\n-------\n + next + } + ++total_records_compared_against_patterns + + # Only print once, even if matches multiple table entries + do_print = 0 + any_match = 0 + + # message ... remove trailing whitepace and blank lines + msg = matches[2] + sub(/( *(\r?\n))+$/, "", msg) + + # try each pattern in the table + for (i in patterns) { + if (msg ~ patterns[i]) { + any_match = 1 + if (found[i]++ < limits[i]) { + do_print = 1 + } + } + } + if (any_match) { + ++total_records_matched_patterns + } + if (do_print) { + print "\nvvvvvvvvvvvvvvvv\n" matches[0] "\n--------\n" matches[1] "\n--------\n`" msg "`\n^^^^^^^^^^^^^^^^\n" + } + } + END { + # Print table header and separator line + printf "Searched %d (%d) records, %d records matched a pattern\n\n", total_records_compared_against_patterns, total_records, total_records_matched_patterns + printf " Count | Pattern\n" + printf "--------|---------------------\n", found[i], patterns[i] + for (i in patterns) { + # Print found count, right-aligned 7 characters, followed by pattern + printf "%7d | %s\n", found[i], patterns[i] + } + } + ' +``` + +### Print ONLY the commit message + +#### Starting from the table-based solution.... + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' ' + BEGIN { + # Table for easier testing + # Set limits[i] to 0 to suppress printing altogether, else count of matches that print + # Set found[i] to 0 + # Set patterns[i] to the regex-like match pattern + i = 0 + limits[i] = 0; found[i] = 0; patterns[i++] = "^make style$" + limits[i] = 0; found[i] = 0; patterns[i++] = "^make miscchecks$" + limits[i] = 0; found[i] = 0; patterns[i++] = "^cppcheck" + total_pattern_count = i + total_records = 0 + total_records_compared_against_patterns = 0 + total_records_matched_patterns = 0 + } + { + ++total_records + if (!match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) { + # Turns out only whitespace ... so false positive? + # print "FAIL: \n------\n" $0 "\n-------\n + next + } + ++total_records_compared_against_patterns + + # Only print once, even if matches multiple table entries + do_print = 0 + any_match = 0 + + # message ... remove trailing whitepace and blank lines + msg = matches[2] + sub(/( *(\r?\n))+$/, "", msg) + + # try each pattern in the table + for (i in patterns) { + if (msg ~ patterns[i]) { + any_match = 1 + if (found[i]++ < limits[i]) { + do_print = 1 + } + } + } + if (any_match) { + ++total_records_matched_patterns + } + if (do_print) { + print "\nvvvvvvvvvvvvvvvv\n" matches[0] "\n--------\n" matches[1] "\n--------\n`" msg "`\n^^^^^^^^^^^^^^^^\n" + } + } + END { + # Print table header and separator line + printf "Searched %d (%d) records, %d records matched a pattern\n\n", total_records_compared_against_patterns, total_records, total_records_matched_patterns + printf " Count | Pattern\n" + printf "--------|---------------------\n", found[i], patterns[i] + for (i in patterns) { + # Print found count, right-aligned 7 characters, followed by pattern + printf "%7d | %s\n", found[i], patterns[i] + } + } + ' +``` + +#### Reduce it + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' ' + BEGIN { + i = 0 + patterns[i++] = "^make style$" + patterns[i++] = "^make miscchecks$" + patterns[i++] = "^cppcheck" + } + { + if (!match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) next + + # Only print once, even if matches multiple table entries + do_print = 0 + + # message ... remove trailing whitepace and blank lines + msg = matches[2] + sub(/( *(\r?\n))+$/, "", msg) + + # try each pattern in the table + for (i in patterns) { + if (msg ~ patterns[i]) { + do_print = 1 + } + } + if (do_print) { + print matches[1] + } + } +' +``` + +## Final Solution + +``` +clear; git log --format='%H%n%B%x00' | awk -v RS='\0' ' + BEGIN { + i = 0 + patterns[i++] = "^make style$" + patterns[i++] = "^make miscchecks$" + patterns[i++] = "^cppcheck" + } + { + if (!match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) next + + # Only print once, even if matches multiple table entries + do_print = 0 + + # message ... remove trailing whitepace and blank lines + msg = matches[2] + sub(/( *(\r?\n))+$/, "", msg) + + # try each pattern in the table + for (i in patterns) { + if (msg ~ patterns[i]) { + do_print = 1 + } + } + if (do_print) { + print matches[1] + } + } +' | sort --unique --ignore-case > .git-blame-ignore-revs +``` diff --git a/filter_git_blame.sh b/filter_git_blame.sh new file mode 100755 index 000000000..2a558970d --- /dev/null +++ b/filter_git_blame.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -e + +git log --format='%H%n%B%x00' | awk -v RS='\0' ' + BEGIN { + i = 0 + patterns[i++] = "^make style$" + patterns[i++] = "^make miscchecks$" + patterns[i++] = "^cppcheck" + } + { + if (!match($0, /^\n?([0-9A-Fa-f]{40})\n(.*)$/, matches)) next + + # Only print once, even if matches multiple table entries + do_print = 0 + + # message ... remove trailing whitepace and blank lines + msg = matches[2] + sub(/( *(\r?\n))+$/, "", msg) + + # try each pattern in the table + for (i in patterns) { + if (msg ~ patterns[i]) { + do_print = 1 + } + } + if (do_print) { + print matches[1] + } + } +' | sort --unique --ignore-case > .git-blame-ignore-revs