{"id":63435,"date":"2022-04-06T09:00:47","date_gmt":"2022-04-06T09:00:47","guid":{"rendered":"https:\/\/www.cryptocabaret.com\/?p=63435"},"modified":"2022-04-06T09:00:47","modified_gmt":"2022-04-06T09:00:47","slug":"make-your-own-git-subcommands","status":"publish","type":"post","link":"https:\/\/www.cryptocabaret.com\/?p=63435","title":{"rendered":"Make your own Git subcommands"},"content":{"rendered":"<p><span class=\"field field--name-title field--type-string field--label-hidden\">Make your own Git subcommands<\/span><br \/>\n<span class=\"field field--name-uid field--type-entity-reference field--label-hidden\"><a title=\"View user profile.\" href=\"https:\/\/opensource.com\/users\/seth\" class=\"username\">Seth Kenlon<\/a><\/span><br \/>\n<span class=\"field field--name-created field--type-created field--label-hidden\">Wed, 04\/06\/2022 &#8211; 03:00<\/span><\/p>\n<div data-drupal-selector=\"rate-node-69711\" class=\"rate-widget-thumbs-up\">\n<div class=\"rate-thumbs-up-btn-up rate-thumbs-up-btn-up vote-pending\">Up<\/div>\n<div class=\"rate-score\"><a href=\"https:\/\/opensource.com\/user\/register?absolute=1\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml&amp;absolute=1\">Login<\/a> to like.<\/div>\n<\/div>\n<div class=\"clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item\">\n<p>Git is pretty famous for having lots of subcommands, like <code>clone<\/code>, <code>init<\/code>, <code>add<\/code>, <code>mv<\/code>, <code>restore<\/code>, <code>bisect<\/code>, <code>blame<\/code>, <code>show<\/code>, <code>rebase<\/code>, and many more. In a previous article, I wrote about the very useful <a href=\"https:\/\/opensource.com\/article\/22\/3\/peek-inside-your-git-repo-rev-parse\">rev-parse subcommand<\/a> for Git. Even with all of these subcommands available, users still come up with functions to improve their Git experience. While you&#8217;re free to create Git-related commands and run them as scripts, it&#8217;s easy to make your own custom Git subcommands. You can even integrate them with Git through <code>rev-parse<\/code>.<\/p>\n<\/p>\n<div class=\"embedded-resource-list callout-float-right\">\n<div class=\"field field--name-title field--type-string field--label-hidden field__item\">More on Git<\/div>\n<div class=\"field field--name-links field--type-link field--label-hidden field__items\">\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/resources\/what-is-git?intcmp=7013a000002CxqLAAS\">What is Git?<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/downloads\/cheat-sheet-git?intcmp=7013a000002CxqLAAS\">Git cheat sheet<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/downloads\/cheat-sheet-markdown?intcmp=7013a000002CxqLAAS\">Markdown cheat sheet<\/a><\/div>\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/tags\/git?intcmp=7013a000002CxqLAAS\">New Git articles<\/a><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<h2>Create a simple Git script<\/h2>\n<p>A script that integrates with Git can be as complex as you need it to be, or it can be short and straightforward.<\/p>\n<p>As a simple example, assume you&#8217;ve created a script that gathers the file names of your latest commit and places them into a file called <code>latest.txt<\/code>. You use this script after every commit for reporting purposes, and you&#8217;ve decided it would be handy to be able to run the script as if it were a built-in feature of Git, for instance, with the command <code>git report<\/code>.<\/p>\n<p>Currently, running <code>git report<\/code> renders this error:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw2\">git<\/span> report<br>\ngit: <span class=\"st_h\">'report'<\/span> is not a <span class=\"kw2\">git<\/span> command. See <span class=\"st_h\">'git --help'<\/span>.<br><br>\nThe most similar <span class=\"kw3\">command<\/span> is<br>\n\u00a0 \u00a0 \u00a0 \u00a0 bugreport<\/div><\/div><\/pre>\n<p>Here&#8217;s the script that generates your report:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co0\">#!\/bin\/sh<\/span><br><br><span class=\"re2\">TOP<\/span>=$<span class=\"br0\">(<\/span><span class=\"kw2\">git rev-parse<\/span> --show-toplevel<span class=\"br0\">)<\/span><br><span class=\"re2\">HASH<\/span>=$<span class=\"br0\">(<\/span><span class=\"kw2\">git log<\/span> <span class=\"re5\">--pretty<\/span>=format:<span class=\"st_h\">'%h'<\/span> <span class=\"re5\">-n<\/span> <span class=\"nu0\">1<\/span><span class=\"br0\">)<\/span><br><br><span class=\"kw2\">mkdir<\/span> <span class=\"st0\">\"<span class=\"es3\">${TOP}<\/span>\"<\/span><span class=\"sy0\">\/<\/span>reports <span class=\"sy0\">||<\/span> <span class=\"kw2\">true<\/span><br><br><span class=\"kw2\">git diff-tree<\/span> <br><span class=\"re5\">--no-commit-id<\/span> <span class=\"re5\">--name-only<\/span> <br><span class=\"re5\">-r<\/span> HEAD <span class=\"sy0\">&gt;<\/span> <span class=\"st0\">\"<span class=\"es3\">${TOP}<\/span>\"<\/span><span class=\"sy0\">\/<\/span>reports<span class=\"sy0\">\/<\/span><span class=\"re1\">$HASH<\/span><\/div><\/div><\/pre>\n<p>Save this file as <code>git-report.sh<\/code> <a href=\"https:\/\/opensource.com\/article\/17\/6\/set-path-linux\">somewhere in your PATH<\/a>.<\/p>\n<p>You&#8217;re not going to run this script directly, so do include the <code>.sh<\/code> extension in the name. Make the script executable:<\/p>\n<pre>\n<span class=\"geshifilter\"><code class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span><span class=\"kw2\">chmod<\/span> +x git-report.sh<\/code><\/span><\/pre>\n<h2>Create the front-end command<\/h2>\n<p>You can force Git to run <code>git report<\/code> through <code>rev-parse<\/code> and launch your <code>git-report.sh<\/code> script instead of returning an error. Here&#8217;s the script:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co0\">#!\/bin\/sh<\/span><br><br>\ngit-report.sh<\/div><\/div><\/pre>\n<p>Save this file as <code>git-report<\/code> (do not use a <code>.sh<\/code> file extension) somewhere on your PATH. Make the script executable:<\/p>\n<pre>\n<span class=\"geshifilter\"><code class=\"bash geshifilter-bash\"><span class=\"co4\">$ <\/span><span class=\"kw2\">chmod<\/span> +x git-report<\/code><\/span><\/pre>\n<h2>Running your custom Git command<\/h2>\n<p>Now test it out. First, create the infrastructure and some sample data:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw2\">mkdir<\/span> myproject ; <span class=\"kw3\">cd<\/span> <span class=\"sy0\">!<\/span>$<br>\n$ <span class=\"kw2\">git init<\/span><br>\n$ <span class=\"kw3\">echo<\/span> <span class=\"st0\">\"foo\"<\/span> <span class=\"sy0\">&gt;<\/span> hello.txt<br>\n$ <span class=\"kw2\">git add<\/span> hello.txt<br>\n$ <span class=\"kw2\">git commit<\/span> <span class=\"re5\">-m<\/span> <span class=\"st_h\">'first file'<\/span><br>\n$ <span class=\"kw2\">git<\/span> report<\/div><\/div><\/pre>\n<p>Look inside the <code>reports<\/code> directory to see a record of your latest commit:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw2\">cat<\/span> reports<span class=\"sy0\">\/<\/span>2e3efd8<br>\nhello.txt<\/div><\/div><\/pre>\n<h2>Pass arguments to your script<\/h2>\n<p>You can pass arguments through <code>rev-parse<\/code>, too. I help maintain a Git subcommand called <a href=\"https:\/\/opensource.com\/article\/19\/4\/manage-multimedia-files-git\">Git-portal<\/a>, which helps manage large multimedia files associated with a Git repository.<\/p>\n<p>It&#8217;s a little like Git LFS or Git Annex, except without the overhead of versioning the actual media files. (The symlinks to the files are kept under version control, but the contents of the files are treated independently, which is useful in scenarios where the artistic process is distinct from the development process.)<\/p>\n<p>To integrate Git-portal with Git as a subcommand, I use a simple front-end shell script, which in turn invokes <code>rev-parse<\/code> for literal parsing.<\/p>\n<p>The example script provided in this article can&#8217;t take any parameters, but the actual Git scripts I write almost always do. Here&#8217;s how arguments are passed, for example, to Git-portal:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co0\">#!\/bin\/sh<\/span><br><br><span class=\"re2\">ARG<\/span>=$<span class=\"br0\">(<\/span><span class=\"kw2\">git rev-parse<\/span> <span class=\"re5\">--sq-quote<\/span> <span class=\"st0\">\"$@\"<\/span><span class=\"br0\">)<\/span><br><span class=\"re2\">CMD<\/span>=<span class=\"st0\">\"git-portal.sh <span class=\"es2\">$ARG<\/span>\"<\/span><br><span class=\"kw3\">eval<\/span> <span class=\"st0\">\"<span class=\"es2\">$CMD<\/span>\"<\/span><\/div><\/div><\/pre>\n<p>The <code>--sq-quote<\/code> option quotes all arguments following <code>git portal<\/code> and includes them as new parameters for <code>git-portal.sh<\/code>, which is the script with all the useful functions in it. A new command is assembled into the <code>CMD<\/code> variable, and then that variable is evaluated and executed.<\/p>\n<p>Here&#8217;s a simple example you can run. It&#8217;s a modified version of a script I use to resize and apply accurate copyright (or copyleft, as the case may be) EXIF data to images for a flat-file CMS I use Git to manage.<\/p>\n<p>This is a simplified example, and there&#8217;s no good reason for this script to be a Git subcommand, but it&#8217;s demonstrative. You can use your imagination to find ways to expand it to use Git functions.<\/p>\n<p>Call this file <code>git-imager.sh<\/code>:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co0\">#!\/bin\/sh<\/span><br><span class=\"co0\">## Requires<\/span><br><span class=\"co0\"># GNU Bash<\/span><br><span class=\"co0\"># exiftool (sometimes packaged as perl-Image-exiftool)<\/span><br><span class=\"co0\"># Image Magick<\/span><br><br><span class=\"re2\">PIC<\/span>=<span class=\"st0\">\"<span class=\"es3\">${1}<\/span>\"<\/span><br><span class=\"re2\">COPY<\/span>=<span class=\"st0\">\"-Copyright\"<\/span><br><span class=\"re2\">LEFT<\/span>=<span class=\"st0\">\"<span class=\"es3\">${2}<\/span>\"<\/span><br><br>\nmogrify <span class=\"re5\">-geometry<\/span> <span class=\"nu0\">512<\/span>^x512 <span class=\"re5\">-gravity<\/span> Center <br>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"re5\">-crop<\/span> 512x512+<span class=\"nu0\">0<\/span>+<span class=\"nu0\">0<\/span> <span class=\"st0\">\"<span class=\"es3\">${1}<\/span>\"<\/span><br><br>\nexiftool <span class=\"re5\">-Copyright<\/span>=<span class=\"st0\">\"<span class=\"es3\">${2}<\/span>\"<\/span> <span class=\"st0\">\"<span class=\"es3\">${1}<\/span>\"<\/span><\/div><\/div><\/pre>\n<p>The front-end script for Git integration passes all parameters (the file name and the license) through to the <code>git-imager.sh<\/code> script.<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\"><span class=\"co0\">#!\/bin\/sh<\/span><br><br><span class=\"re2\">ARG<\/span>=$<span class=\"br0\">(<\/span><span class=\"kw2\">git rev-parse<\/span> <span class=\"re5\">--sq-quote<\/span> <span class=\"st0\">\"$@\"<\/span><span class=\"br0\">)<\/span><br><span class=\"re2\">CMD<\/span>=<span class=\"st0\">\"git-imager.sh <span class=\"es2\">$ARG<\/span>\"<\/span><br><span class=\"kw3\">eval<\/span> <span class=\"st0\">\"<span class=\"es2\">$CMD<\/span>\"<\/span><\/div><\/div><\/pre>\n<p>Try it out:<\/p>\n<pre>\n<div class=\"geshifilter\"><div class=\"bash geshifilter-bash\">$ <span class=\"kw2\">git<\/span> imager logo.jpg <span class=\"st0\">\"Tux CC BY-SA\"<\/span><br>\n\u00a0<span class=\"nu0\">1<\/span> image files updated<\/div><\/div><\/pre>\n<h2>Easy Git customization<\/h2>\n<p>Creating your own Git subcommand makes your custom scripts feel like natural components of Git. For users, it makes the new subcommands easy to remember, and it helps your subcommands integrate into the rest of everyone&#8217;s Git workflow.<\/p>\n<\/div>\n<div class=\"clearfix text-formatted field field--name-field-article-subhead field--type-text-long field--label-hidden field__item\">\n<p>Creating your own Git subcommand makes your custom scripts feel like natural components of Git.<\/p>\n<\/div>\n<div class=\"field field--name-field-lead-image field--type-entity-reference field--label-hidden field__item\">\n<article class=\"media media--type-image media--view-mode-caption\">\n<div class=\"field field--name-field-media-image field--type-image field--label-hidden field__item\">  <img decoding=\"async\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/04\/OSDC_women_computing_2.png\" width=\"520\" height=\"292\" alt=\"Woman sitting in front of her computer\" title=\"Women in computing and open source\" loading=\"lazy\"><\/div>\n<div class=\"field field--name-field-caption field--type-text-long field--label-hidden caption field__item\"><span class=\"caption__byline\">Image by: <\/span><\/p>\n<p>Ray Smith<\/p>\n<\/div>\n<\/article>\n<\/div>\n<div class=\"field field--name-field-tags field--type-entity-reference field--label-hidden field__items\">\n<div class=\"field__item\"><a href=\"https:\/\/opensource.com\/tags\/git\" hreflang=\"en\">Git<\/a><\/div>\n<\/p><\/div>\n<div class=\"hidden field field--name-field-listicle-title field--type-string field--label-hidden field__item\">What to read next<\/div>\n<div class=\"field field--name-field-default-license field--type-list-string field--label-hidden field__item\"><a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/4.0\/\"><br \/>\n        <img decoding=\"async\" alt=\"Creative Commons License\" src=\"https:\/\/www.cryptocabaret.com\/wp-content\/uploads\/2022\/04\/cc-by-sa-4-8.png\" title=\"This work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.\"><\/a>This work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.<\/div>\n<section class=\"field field--name-field-comments field--type-comment field--label-hidden comment-wrapper\">\n<div class=\"comments__count\">\n<div class=\"login\"><a href=\"https:\/\/opensource.com\/user\/register?absolute=1\">Register<\/a> or <a href=\"https:\/\/opensource.com\/user\/login?current=\/rss.xml&amp;absolute=1\">Login<\/a> to post a comment.<\/div>\n<\/p><\/div>\n<\/section>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\" rel=\"noopener\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Make your own Git subcommands Seth Kenlon Wed, 04\/06\/2022 &#8211; 03:00 Up Register or Login to like. Git is pretty famous for having lots of subcommands, like clone, init, add, mv, restore, bisect, blame, show, rebase, and many more. In a previous article, I wrote about the very useful rev-parse subcommand for Git. Even with [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":63436,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[307],"tags":[],"class_list":["post-63435","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-open-source"],"_links":{"self":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts\/63435","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=63435"}],"version-history":[{"count":0,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/posts\/63435\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=\/wp\/v2\/media\/63436"}],"wp:attachment":[{"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=63435"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=63435"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cryptocabaret.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=63435"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}