How to list all commits with gitlib? - gitlib

How to list commits with gitlib in a similar fashion to git log --reflog command? So that it would list not only HEAD commits, but all commits that are there in the repository.
I have following code in Haskell:
reverseCommitLookup :: RefName -> ReaderT LgRepo IO ()
reverseCommitLookup refName = do
maybeReference <- resolveReference refName
lift $ print $ T.unpack refName ++ " => "
case maybeReference of
Nothing -> liftIO $ print "Cannot resolve the reference"
Just ref -> do
obj <- lookupObject ref
case obj of
CommitObj commit -> do
commits <- listCommits Nothing (commitOid commit)
for_ commits (\commitOid -> do
ci <- lookupCommit commitOid
tree <- lookupTree $ commitTree ci
maybeEntry <- treeEntry tree $ BS.pack contentFile
case maybeEntry of
Nothing -> liftIO $ print "No content"
Just entry -> do
content <- catBlob $ blobEntryOid entry
liftIO $ print $ (T.unpack $ renderObjOid commitOid) ++ ": " ++ (T.unpack $ commitLog $ ci ) ++ " -> " ++ (BS.unpack content) ++ " (" ++ (show $ (floor $ utcTimeToPOSIXSeconds $ zonedTimeToUTC $ signatureWhen $ commitAuthor ci :: Int)) ++ ") ")
_ -> liftIO $ print "Not a commit"
It is able to output commits for a given reference. For example, HEAD or refs/heads/master, etc:
repositoryOptions :: RepositoryOptions
repositoryOptions = RepositoryOptions { repoPath = "data/.git" , repoWorkingDir = Just "data", repoIsBare = False , repoAutoCreate = True}
main :: IO ()
main = do
repo <- openRepository lgFactory repositoryOptions
runReaderT (reverseCommitLookup $ T.pack "HEAD") repo
runReaderT (reverseCommitLookup $ T.pack "refs/heads/master") repo
What it is not capable to do is to output commits historically as they appear in git repository history as git log --reflog shows. For example, there might be other commits to other branches, not only to HEAD or refs/heads/master and I want them to appear in output at the time they were created relatively to other commits in whole repository history. How could I achieve that?

I'm not a gitlib expert, but I am a Haskell expert and I have taken a quick look through the gitlib haddocks. My conclusion is that this is a "patches welcome" sort of situation.

Related

Syntax conflict for "{" using Nextflow

New to nextflow, attempted to run a loop in nextflow chunk to remove extension from sequence file names and am running into a syntax error.
params.rename = "sequences/*.fastq.gz"
workflow {
rename_ch = Channel.fromPath(params.rename)
RENAME(rename_ch)
RENAME.out.view()
}
process RENAME {
input:
path read
output:
stdout
script:
"""
for file in $baseDir/sequences/*.fastq.gz;
do
mv -- '$file' '${file%%.fastq.gz}'
done
"""
}
Error:
- cause: Unexpected input: '{' # line 25, column 16.
process RENAME {
^
Tried to use other methods such as basename, but to no avail.
Inside a script block, you just need to escape the Bash dollar-variables and use double quotes so that they can expand. For example:
params.rename = "sequences/*.fastq.gz"
workflow {
RENAME()
}
process RENAME {
debug true
"""
for fastq in ${baseDir}/sequences/*.fastq.gz;
do
echo mv -- "\$fastq" "\${fastq%%.fastq.gz}"
done
"""
}
Results:
$ nextflow run main.nf
N E X T F L O W ~ version 22.04.0
Launching `main.nf` [crazy_brown] DSL2 - revision: 71ada7b0d5
executor > local (1)
[71/4321e6] process > RENAME [100%] 1 of 1 ✔
mv -- /path/to/sequences/A.fastq.gz /path/to/sequences/A
mv -- /path/to/sequences/B.fastq.gz /path/to/sequences/B
mv -- /path/to/sequences/C.fastq.gz /path/to/sequences/C
Also, if you find escaping the Bash variables tedious, you may want to consider using a shell block instead.

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
}

With "go list" how to list only Go modules used in the binary?

I want to list the modules (and their versions) that are compiled in the final executable (and not other dependencies).
I can do that with:
$ go build -o a.out
$ go version -m a.out
But how can I do that with go list (which has a convenient JSON output)?
I tried this:
$ go list -m -f '{{define "M"}}{{.Path}}#{{.Version}}{{end}}{{if not .Main}}{{if .Replace}}{{template "M" .Replace}}{{else}}{{template "M" .}}{{end}}{{end}}' all
But it lists many transitive dependencies which are only used in test suites for example.
I don't see how I could filter out those dependencies.
Here is a sample project to see the problem (available on The Go Playground):
main.go:
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
main_test.go:
package main
import (
"github.com/google/go-cmp/cmp"
"testing"
)
func TestHelloWorld(t *testing.T) {
if !cmp.Equal(1, 1) {
t.Fatal("FAIL")
}
}
go.mod:
module play.ground
go 1.15
require github.com/google/go-cmp v0.5.2
$ go build -o hello ; go version -m hello
hello: go1.15
path play.ground
mod play.ground (devel)
$ go list -m -f '{{define "M"}}{{.Path}}#{{.Version}}{{end}}{{if not .Main}}{{if .Replace}}{{template "M" .Replace}}{{else}}{{template "M" .}}{{end}}{{end}}' all
github.com/google/go-cmp#v0.5.2
golang.org/x/xerrors#v0.0.0-20191204190536-9bdfabe68543
Here is the answer:
go list -deps -f '{{define "M"}}{{.Path}}#{{.Version}}{{end}}{{with .Module}}{{if not .Main}}{{if .Replace}}{{template "M" .Replace}}{{else}}{{template "M" .}}{{end}}{{end}}{{end}}' | sort -u
Note: go list -deps produces one row for each package. So modules from which multiple packages are imported are listed mutiple times. sort -u sorts and removes duplicates.
It can be compared with:
go version -m hello | perl -ne 's/^\tdep\t([^\t]*)\t([^\t]*).*$/$1\#$2/ && print' | sort
Here is a version with more details that lists each package referenced from each module (also using jq:
go list -deps -f '{{define "mod"}}{{.Path}}#{{.Version}}{{end}}{{if .Module}}{{if not .Module.Main}}{{if .Module.Replace}}{{template "mod" .Module.Replace}}{{else}}{{template "mod" .Module}}{{end}}{{"\t"}}{{.ImportPath}}{{end}}{{end}}' | sort
go list -deps -json | jq -r 'select(.Module and (.Module.Main | not)) | .Module.Path + "#" + .Module.Version + "\t" + .ImportPath' | sort

How to get the diff between two hashes in 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

Haskell - System.Process gives an error when password is required

I am creating a Haskell program to automatically add a user to the system and then add it to samba, but it asks password, and I would like to use the same. I did not find any parameter we can use with smbclient to achieve such goal. What can I do?
Here is my code:
import System.Environment(getArgs)
import System.Process (createProcess, shell)
main = do
(user:pass:_) <- getArgs
putStrLn $ "Creating user " ++ user ++ " " ++ pass ++ "..."
callCommand $ "useradd " ++ user
callCommand $ "echo \"" ++ user ++ "\":\"" ++ pass ++ "\" | chpasswd"
putStrLn $ "User Created. Adding it to samba..."
callCommand $ "smbpasswd -a " ++ user
callCommand = createProcess . shell
Result:
-bash# runghc test.hs testUser 12345
Creating user testUser 12345...
User Created. Adding it to samba...
chpasswd: (user testUser) pam_chauthtok() failed, error:
Authentication token manipulation error
chpasswd: (line 1, user testUser) password not changed
-bash# New SMB password:
Retype new SMB password:
Failed to add entry for user testUser.
Can I pass the pass argument received from user to the smbclient program (or press Enter automatically), and avoid this error?
Use readCreateProcess:
readCreateProcess :: CreateProcess -> String -> IO String
The additional String will be passed to the command as standard input.

Resources