How to get the diff between two hashes in gitpython - gitpython

At the command line I can do
git diff --name-only <hashA> <hashB>
to enumerate all the changed files. How would I do this in gitpython?

Here's a technique I found snooping around the source to connect a git hash/sha to a particular gitpython Commit object.
# test_git_diff.py
import git
#remote_repo="https://github.com/jxramos/DataApp--ParamCompare/commits/master"
git_repo = git.Repo( "." )
source_hash = '825faef1097207479f968c6a5353e41612127849'
target_hash = 'HEAD'
source_commit = git_repo.commit( source_hash )
target_commit = git_repo.commit( target_hash )
git_diff = source_commit.diff( target_commit )
changed_files = [ f.b_path for f in git_diff ]
print( "\n".join( changed_files ))
Comparing side by side with shell output
> python test_git_diff.py
Model.py
README.md
data_app.py
param_plotting.py
param_stats.py
session_info.py
static/summary_page_styles.css
templates/data_explore_page.html
templates/embed.html
templates/lin_reg_result.html
templates/plot_page.html
templates/summary_page.html
test/test_data/1.x.csv
test/test_data/1.y.csv
>
>
> git diff --name-only 825faef1097207479f968c6a5353e41612127849 HEAD
Model.py
README.md
data_app.py
param_plotting.py
param_stats.py
session_info.py
static/summary_page_styles.css
templates/data_explore_page.html
templates/embed.html
templates/lin_reg_result.html
templates/plot_page.html
templates/summary_page.html
test/test_data/1.x.csv
test/test_data/1.y.csv

Related

How to capture last part of the git url using shell script?

I am writing a Jenkins pipeline. I am trying to capture last part of the git url without the git extension. For instance: https://github.hhhh.com/aaaaaa-dddd/xxxx-yyyy.git. I want only xxxx-yyyy to be returned. Below is my code:
String getProjectName() {
echo "inside getProjectName +++++++"
# projectName = sh(
# script: "git config --get remote.origin.url",
# returnStdout: true
# ).trim()
def projectName= sh returnStdout:true, script: '''
#!/bin/bash
GIT_LOG = $(env -i git config --get remote.origin.url)
echo $GIT_LOG
basename -s .git "$GIT_LOG"; '''
echo "projectName: ${projectName}"
return projectName
}
PS: Please ignore the commented lines of code.
There is basic Bourne shell functionality that achieves that:
# strip everything up to the last /
projectName=${GIT_LOG##*/}
# strip trailing .git
projectName=${projectName%.git}
This leaves just the requested name in projectName.
No space before and after =:
x='https://github.hhhh.com/aaaaaa-dddd/xxxx-yyyy.git'
basename "$x" .git
Output:
xxxx-yyyy

How to trim part of git rev-parse output from windows slave

I have the below output from my jenkinsfile.groovy (running on windows slave):
command:
def commitHash = bat(returnStdout: true, script: "git rev-parse HEAD").trim()
commitHash content:
c:\jenkins-slave\workspace\test-K5I54FOWDXJU7QWEX2YF4ZSWVNSFITDVMLAIK3SVMG3V4JJM2QHA>git rev-parse HEAD 123456
How can I get from it only 123456?
This is similar to JENKINS-44569
def getCommandOutput(cmd) {
if (isUnix()){
return sh(returnStdout:true , script: '#!/bin/sh -e\n' + cmd).trim()
} else{
stdout = bat(returnStdout:true , script: cmd).trim()
result = stdout.readLines().drop(1).join(" ")
return result
}
}
That or adding #echo off to the command, as seen here (and commented below)
env.gitcurrent= \
bat(returnStdout: true, script: "#echo off | git --git-dir=${WORKSPACE}\\.git rev-parse HEAD 2> nul || echo githash").trim()

Git completion for alias as if for Git itself

Background
I have successfully configured Bash completion for various Git aliases. For example:
$ git config alias.subject
!git --no-pager show --quiet --pretty='%s'
$ function _git_subject() { _git_show; }
$ git subject my<TAB>
$ git subject my-branch
Challenge
However, I have a Git alias that I don't know how to set up Bash completion for. The problem is that I want the alias to complete as if for the top-level Git command itself. The alias is this:
$ git config alias.alias
alias = !"f() { if [[ \"$#\" != 1 ]]; then >&2 echo \"Usage: git alias COMMAND\"; return 1; fi; git config alias.\"$1\"; }; f"
# Example
$ git alias s
status
I have tried using _git, __git_main, and __git_wrap__git_main, but none of them work (I think it leads to an infinite loop since it never returns after I press tab).
Is there a way to add completion for a Git alias that completes as if it was the top-level Git command? Or specifically how to have completion for this alias?
Tried but doesn't work
function _git_alias() { _git; }
function _git_alias() { __git_main; }
function _git_alias() { __git_wrap__git_main; }
Desired behavior
$ git alias su<TAB>
subject submodule
$ git alias sub
Alternatively, if there's an easy way to complete for only aliases that would be cool, too. I would like to know how to complete as if for the top-level Git command just for curiosity as well, though.
I was finally able to create a working solution with a bit of hackery around the "magic" Bash completion variables. I changed these variables to "pretend" we were completing the given command as given to git itself.
If anybody has any suggestions to simplify this I would totally be open to suggestions.
# This is complex because we want to delegate to the completion for Git
# itself without ending up with an infinite loop (which happens if you try
# to just delegate to _git).
_git_alias() {
if [[ "$COMP_CWORD" -lt 2 ]]; then
return
fi
local old_comp_line_length new_comp_line_length
COMP_WORDS=(git "${COMP_WORDS[#]:2}")
((COMP_CWORD -= 1))
old_comp_line_length=${#COMP_LINE}
if [[ "$COMP_LINE" =~ ^[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+(.*)$ ]]; then
COMP_LINE="git ${BASH_REMATCH[1]}"
fi
new_comp_line_length=${#COMP_LINE}
(( COMP_POINT += new_comp_line_length - old_comp_line_length ))
_git "$#"
# git alias blah
# ^
# 01234567890123
# 0 1
# point: 11
# length: 13
#
# git blah
# ^
# 01234567
# point: 5
# length: 7
#
# point = point - (old length) + (new length)
# point = 11 - 13 + 7
# point = -2 + 7
# point = 5
}

Convert .gitmodules into a parsable format for iteration using Bash

Background
I would like to make a shell function that takes .gitmodules and iterates over each module executing certain commands based off of each submodules properties (e.g. <PATH> or <URL> or <BRANCH>).
➡️ The default format of .gitmodules:
[submodule "PATH"]
path = <PATH>
url = <URL>
[submodule "PATH"]
path = <PATH>
url = <URL>
branch = <BRANCH>
➡️ Pseudocode:
def install_modules() {
modules = new list
fill each index of the modules list with each submodule & its properties
iteratate over modules
if module # 'path' contains a specified 'branch':
git submodule add -b 'branch' 'url' 'path'
else:
git submodule add 'url' 'path'
}
⚠️ Current install_modules()
# currently works for grabbing the first line of the file
# doesn't work for each line after.
install_modules() {
declare -A regex
regex["module"]='\[submodule "(.*)"\]'
regex["url"]='url = "(.*)"'
regex["branch"]='branch = "(.*)"'
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cat < ".gitmodules" | while read -r LINE; do
if [[ $LINE =~ ${regex[module]} ]]; then
PATH=${BASH_REMATCH[1]}
echo "$PATH"
fi
done
}
.gitmodules is a .gitconfig-like file so you can use git config to read it. For example, read all values from a .gitmodules, split values by = (key=value), and split keys by .:
git config -f .gitmodules -l | awk '{split($0, a, /=/); split(a[1], b, /\./); print b[1], b[2], b[3], a[2]}'
git config -f .gitmodules -l prints something like
submodule.native/inotify_simple.path=native/inotify_simple
submodule.native/inotify_simple.url=https://github.com/chrisjbillington/inotify_simple
and awk output would be
submodule native/inotify_simple path native/inotify_simple
submodule native/inotify_simple url https://github.com/chrisjbillington/inotify_simple
With a little help from #phd and Restore git submodules from .gitmodules (which #phd pointed me towards), I was able to construct the function that I needed.
install_submodules()
⚠️ Note: Assume $REPO_PATH is declared & initialized.
⚠️ My answer is an adaptation from https://stackoverflow.com/a/53269641/5290011.
install_submodules() {
git -C "${REPO_PATH}" config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
while read -r KEY MODULE_PATH
do
# If the module's path exists, remove it.
# This is done b/c the module's path is currently
# not a valid git repo and adding the submodule will cause an error.
[ -d "${MODULE_PATH}" ] && sudo rm -rf "${MODULE_PATH}"
NAME="$(echo "${KEY}" | sed 's/^submodule\.\(.*\)\.path$/\1/')"
url_key="$(echo "${KEY}" | sed 's/\.path$/.url/')"
branch_key="$(echo "${KEY}" | sed 's/\.path$/.branch/')"
URL="$(git config -f .gitmodules --get "${url_key}")"
BRANCH="$(git config -f .gitmodules --get "${branch_key}" || echo "master")"
git -C "${REPO_PATH}" submodule add --force -b "${BRANCH}" --name "${NAME}" "${URL}" "${MODULE_PATH}" || continue
done
git -C "${REPO_PATH}" submodule update --init --recursive
}

How to `git diff --name-only master` with rugged?

In Ruby, using the rugged gem, how does one do the equivalent of the following?
%x(git diff --name-only master)
I need to list changed files that are either staged or unstaged.
Here's the solution I came up with:
files0 = %x(git diff --name-only master).split($RS)
require 'rugged'
files1 = []
changed = %i(index_modified index_new worktree_modified worktree_new)
repo = Rugged::Repository.new(Dir.pwd)
repo.status { |f, d| files1 << f unless (changed & d).empty? }
puts(files0.sort == files1.sort ? "PASS" : "FAIL")

Resources