Git config using shell command - macos

I have a alias that does a short status, parses it with sed then adds the files to the 'assume-unchanged' index of git.
However, the issue seems to be a simple problem with my understanding of escaping single quotes in OS X bash.
irm = !sh -c 'git ignore $(git st --short -u | sed '\''/^ D/s/^ D//g'\'')'
This is the full line in gitconfig. I can issue the command in the shell (with sh and the quote), but I get bad git config when I try to run it via git irm
based on advice below, I have configured this a little differently. However, it still doesn't work in gitconfig. So I added this to my ~/.profile
alias irm="git ignore $(git st --short | grep '^ D' | sed 's/^ D //')"

You should be able to use double quotes, but you'll have to escape them:
irm = !sh -c 'git ignore $(git st --short -u | sed \"s/^ D//\")'
You don't need to select the line since the operation is the same as the selection. You may want to use -n and p with sed as Chris suggests in the comment if you only want to output the lines that match and exclude any others.
Also, since the pattern is anchored you don't need the global option.

Related

Transform string into Git branch name format with Bash

Lazy programmer here, I'm making a simple shell script that takes a branch name from the user input, transforms that name into proper format and creates new branch locally, then pushes it to the remote.
So the goal is to transform a string e.g. 'Mary had a little lamb' into 'mary-had-a-little-lamb', removing all characters that aren't digits or letters along the way as well as replacing all spaces, single or multiple, with -.
I have a working solution but it looks pretty ugly to me, how can I improve it?
Also, is there a way to check if the specified branch already exists locally and only proceed if it doesn't?
#!/bin/bash
currentBranch=$(git branch --show-current)
echo "Checking out from branch $currentBranch"
echo "Enter new branch name:"
read branchName
branchName=$(echo $branchName | tr -d ':-') #remove special characters
branchName=$(echo $branchName | tr -s ' ') #replace multiple spaces with one
branchName=$(echo $branchName | tr ' ' '-') #replace spaces with -
branchName=${branchName,,}
echo "Checking out new branch $branchName..."
git checkout -b $branchName
echo "Pushing new branch $branchName to the remote..."
git push origin $branchName
You can use Bash's built-in string substitution:
#!/usr/bin/env bash
# Take this for a test
branch_name='foo bar baz:: ::: qux----corge'
# Need extglob for the pattern to replace
shopt -s extglob
# Do the substition with extglob pattern
#san_branch_name="${branch_name//+([[:punct:][:space:]-])/-}"
# this is a shorter filter for valid git identifiers
san_branch_name="${branch_name//+([^[:alnum:]])/-}"
# For debug purposes
declare -p branch_name san_branch_name
Actual output:
declare -- branch_name="foo bar baz:: ::: qux----corge"
declare -- san_branch_name="foo-bar-baz-qux-corge"
I suggest you to use sed in order to sanitize you branch name by sed as follow:
sanitized_branch_name=$(echo ${branchName} | sed -E 's/\s+/\s/g' | sed -E 's/[\s:]/\-/g')
About how to check branch it is enough:
if git branch -a | grep $sanitized_branch_name 2>& 1>/dev/null; then
echo "${sanitized_branch_name} branch exists!"
fi
Edit (example output):
$ branchName="antonio petri:cca"
$ echo ${branchName} | sed -E 's/\s+/\s/g' | sed -E 's/[\s:]/\-/g'
antonio-petri-cca

sed: Argument list too long when running sed -n

I am running this command from Why is my git repository so big? on a very big git repository as https://github.com/python/cpython
git rev-list --all --objects | sed -n $(git rev-list --objects --all | cut -f1 -d' ' | git cat-file --batch-check | grep blob | sort -n -k 3 | tail -n800 | while read hash type size; do size_in_kibibytes=$(echo $size | awk '{ foo = $1 / 1024 ; print foo "KiB" }'); echo -n "-e s/$hash/$size_in_kibibytes/p "; done) | sort -n -k1;
It works fine if I replace tail -n800 by tail -n40:
1160.94KiB Lib/ensurepip/_bundled/pip-8.0.2-py2.py3-none-any.whl
1169.59KiB Lib/ensurepip/_bundled/pip-8.1.1-py2.py3-none-any.whl
1170.86KiB Lib/ensurepip/_bundled/pip-8.1.2-py2.py3-none-any.whl
1225.24KiB Lib/ensurepip/_bundled/pip-9.0.0-py2.py3-none-any.whl
...
I found this question Bash : sed -n arguments saying I could use awk instead of sed.
Do you know how do fix this sed: Argument list too long when tail is -n800 instead of -n40?
It seems you have used this anwer in the linked question: Some scripts I use:.... There is a telling comment in that answer:
This function is great, but it's unimaginably slow. It can't even finish on my computer if I remove the 40 line limit. FYI, I just added an answer with a more efficient version of this function. Check it out if you want to use this logic on a big repository, or if you want to see the sizes summed per file or per folder. – piojo Jul 28 '17 at 7:59
And luckily piojo has written another answer addressing this. Just use his code.
As an alternative, check if git sizer would work on your repository: that would help isolating what takes place in your repository.
If not, you have other commands in "How to find/identify large commits in git history?", which do loop around each objects and avoid the sed -nxx part
The alternative would be to redirect your result/command to a file, then sed on that file, as in here.

Grep list of files returned from git status

So I am trying to clean up a script I have that gets the list of currently staged files using git status to return the relative file path of each staged file. I have looked at the documentation for git status to see if there was a way to get it to return the absolute path of each staged file, but there doesn't seem to be an option for that and git ls files nor git diff will work for this since the specific use case is during a merge.
During the merge, using git diff returns nothing, while git status does show all of the staged files, so I am stuck using git status.
From this list I have, I want to then grep through the list of files to extract any line(s) that contain the string "Path: " and output it. Basically, I have a list of staged .yml files and I want to extract all changes to the Path property in those ymls. Heres what I have so far:
IFS=$'\n'
for file in `git status -s -uno | sed s/^..//`
do
relativePath=$(echo $file | sed 's/^[ \t]*//;s/[ \t]*$//' | tr -d '"')
startPath=`pwd`
grep "Path: " "$startPath/$relativePath"
done
unset IFS
Explanation:
git status -s -uno | sed s/^..//
I am piping the result of git status into sed to remove any extra whitespace
relativePath=$(echo $file | sed 's/^[ \t]*//;s/[ \t]*$//' | tr -d '"')
I echo the file path and pipe it into sed to remove any extra spaces that weren't removed from the initial sed call in the start of the for loop. I then pipe that into tr to remove the first and last double quotes from the string since I need to combine that relative path with my starting path in order to get the complete path.
startPath=`pwd`
grep "Path: " "$startPath/$relativePath"
Store the current working directory in a variable, then combine it with our relative path and pass that into grep
This script works and extracts the data that I need, but I feel like there is a much cleaner way I could be doing this. Is there a way I can get git status to return the full path of each staged file so I don't have to have my second $startPath variable that I combine with my $relativePath thats passed into grep?
The simplest (correct) way to do this is by using git grep combined with git ls-files. The latter is used as a selector for grep.
Recursive search of modified tracked files using a pattern:
git grep -r 'pattern' -- `git ls-files -m`
Recursive search of all tracked files using a pattern:
git grep -r "pattern" .
Note that this grep search doesn't cover untracked files. You must add them first so that git can see them.
Since you probably call git status from inside the repo, all paths will be relative to $PWD, so you can just add it in place, yes?
$: git status -s | sed "s#^[^ ]* #$PWD/#"

How to use awk inside git --msg-filter

i am trying to clean up some git history. For instance, trim all lines in my commits messages. I need to be able to do something like:
git filter-branch -f --msg-filter 'cat | awk '{$1=$1;print}'' HEAD
this, of course, will fail because of my bad usage of Apostrophes.
It does not work either if I try to escape then or use double apostrophes.
As an example of what I need to process take this:
Add cool service to application
Related: ISSUE-3
This is the result of appending related issue identifier at end of commit and remove it from my summary line, note the space(s) at beggining of commit summary. It is mostly those commit summary what i want to trim with awk.
Can anybody help me with my limited bash skills?
Thanks in advance
In the following command :
git filter-branch -f --msg-filter 'cat | awk '{$1=$1;print}'' HEAD
the expression between the innermost single quotes is not escaped and $1 is replaced by value
git filter-branch -f --msg-filter 'cat | awk '\''{$1=$1;print}'\' HEAD
may be valid. Try also to add echo command at the beginning of the line
echo git filter-branch -f --msg-filter 'cat | awk '{$1=$1;print}'' HEAD
echo git filter-branch -f --msg-filter 'cat | awk '\''{$1=$1;print}'\' HEAD
or clearer adding printf "'%s'\n"
printf "'%s'\n" git filter-branch -f --msg-filter 'cat | awk '\''{$1=$1;print}'\' HEAD

Bash and variables substitution

I use this to generate svn logs for a specific user:
svn log | sed -n '/bob/,/-----$/ p'
which works fine. Now I would like to use a parameter/variable for the user:
USER="bob"
svn log | sed -n '/$USER/,/-----$/ p'
but it just give an empty log. How do I pass a parameter to the above command?
The correct way to do this is to use double quotes so that the bash variable can be expanded:
USER="bob"
svn log | sed -n "/$USER/,/-----$/p"
Adding more single quotes to "insert" a bash variable (as per the accepted answer) is not best practice. HTH.
Use single quotes:
USER="bob"
svn log | sed -n '/'$USER'/,/-----$/ p'
try:
svn log | sed -n '/"$USER"/,/-----$/ p'

Resources