Added a script - cvgitout2line.py

in «tool» by Michael Beard
Tags: , , , , ,

Description

It takes git status lines and converts it to a string with no line feeds, and then adds it to the clipboard for easy copying.

What it solved

This has been a issue with using git when you don't want to have to do a git add . command. Grid has added to it, as I usually have a teamsite directory in my git downstream that I never want to check in, so ...

This makes it so that I can highlight/copy the lines from git, run this script, and then have a line that I can paste after a git command, like git add <paste>. I usually do this by hand, which gets annoying and tedious, so ... Today, I figured that I would write a tool for myself that would fix this for me.

Code or Examples

The file cvgitout2line.py is in my .mike/my_setup/bin directory (see code snippet below). I have an alias set up like so (that uses another alias called mybin) so I don't have to remember much.

You have to remember to copy the git lines into the clipboard so it has something to operate on. It will print the basename of the path given (to give you an idea of what is going to be in the string) but does NOT print out the whole path. If you want that (other than in the clipboard) you can look at the git output.

If it doesn't find the type of lines it needs (really simple right now -- it splits each line on the ":" and if there aren't two parts -- no more or less, then it uses the second part as the file name to append -- this could be modified so that it is more air-tight, but for now, I think it will be fine).

If, at the end of the lines to process, the output is still empty, it will not copy anything to the clipboard and print a message to that affect. Otherwise, it will copy each file, separated by a space, into the clipboard.

Here is the alias:

alias cvg="mybin cvgitout2line.py"

The mybin alias automatically adds my bin directory before the argument I pass it, so I don't have to type in my directory name, but it's almost like it being in my PATH environment variable.

If I was going to call it directly, it would look something like this:

#### for full path to the script to run it
~/.mike/my_setup/bin/cvgitout2line.py

#### using my `mybin` alias
mybin cvgitout2line.py

#### using the alias that I defined above
cvg

Simple and easy.

Here is the code for the file:

#!/usr/bin/env python

import subprocess

from rich.console import Console

import clipboard

# ---------------------------------------

# from https://stackoverflow.com/questions/8384737/extract-file-name-from-path-no-matter-what-the-os-path-format
# using answer from Lauritz V. Thaulow

# the ntpath module (which is equivalent to os.path when running on windows) will 
# work for all(1) paths on all platforms.

import ntpath

def path_leaf(path):
    head, tail = ntpath.split(path)
    return tail or ntpath.basename(head)


# ---------------------------------------

console = Console()

# ---------------------------------------


def processClipboardInput():
    gitOutput = clipboard.read_from_clipboard()

    filesToAdd = ""
    rptFiles = []

    for line in gitOutput.split("\n"):
        keep, fToAdd = shouldAddFile(line)
        if not keep:
            continue
        rptFiles.append(f"    [green]{path_leaf(fToAdd)}[/green]")
        filesToAdd += " " + fToAdd

    rptStr = "\n".join(rptFiles)
    return (filesToAdd, rptStr)


def shouldAddFile(line):
    # make it slightly better in that it checks the first 
    # value (status) to make sure it's one of the expected
    # git values.  These are (from git status docs):
    #     status          action
    #     ------------    -----------------------
    #     unmodified      throw
    #     modified        keep 
    #     added           keep
    #     deleted         throw
    #     renamed         keep
    #     copied          keep
    #     updated         keep
    #     untracked       throw
    #     ignored         throw

    keepStatus = ["modified", "added", "renamed", "copied", "updated", "new file"]

    items = line.split(":")
    if len(items) != 2:
        return (False, "")

    file = items[1].strip()
    if file == "":
        return (False, "")

    status = items[0].strip()
    if status not in keepStatus:
        return (False, "")

    return (True, file)


def success(filesToAdd, rptStr):
    clipboard.write_to_clipboard(filesToAdd)

    msg = "The following files (and their paths, as given by git) will " \
      "be concatenated to a single string in the clipboard, ready to " \
      "be pasted into a 'git add <paste>' command."
    divider = "=========================================="

    console.print(f"[white]{msg}[/white]\n[yellow]{divider}[/yellow]")
    console.print(rptStr)
    console.print(f"[yellow]{divider}[/yellow]")
    console.print("[blue]The file(s) were copied to the clipboard.[/blue]\n")


def noFilesFound():
    msg = "No files detected in clipboard, so NOT overwritting the clipboard. \n"
    console.print("[red]!!!NOTE!!![/red]")
    console.print(f"[yellow]{msg}[/yellow]")


# ---------------------------------------


def main():
    console.print("\n\nConvert git output to single line")
    console.print("----------------------------------------")

    filesToAdd, rptStr = processClipboardInput()
    if len(filesToAdd) <= 0:
        noFilesFound()
        return

    success(filesToAdd, rptStr)


# ---------------------------------------


if __name__ == "__main__":
    main()



# ---------------------------------------


exampleClipboardText = """
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   rtq/common.ftl
    modified:   rtq/determinationQuestions.ftl
    modified:   rtq/subscriptionAgreementNonprofessional.ftl
    modified:   rtq/subscriptionAgreementProfessional.ftl
    deleted:    rtq/notNeededNow.js
    added:      rtq/betterNamedFile.js
    renamed:    rtq/renamedAndNeededNow.js
    copied:     rtq/keep/someCopyOfFile.styl
    madeUp:     rtq/doesNotMatter.xml
    add:        rtq/alsoNotNotedOrMatters.yaml


Untracked files:
  (use "git add <file>..." to include in what will be committed)
    teamsite/
    fred/abc.123
"""