How to parse git diff of specific file using Ruby? - ruby

I have an Android XML (string) file that is edited in a Ruby script. I would like to list and output the changes that were made then. I have tried it with Nokogiri and nokogiri/diff. But it does not have the desired result.
I also have the feeling that it has problems when a new line is added in the middle of it. All in all, I think it would be easiest if I could use git diff.
I've also found ruby-git gem, but I still could not get it to work. Especially because I only need the diff of a specific file.
require 'git'
Git.configure do |config|
#not sure if I actually need something?
end
g = Git.open(path_to_my_dir, :log => Logger.new(STDOUT))
g.diff(path_to_file)
#or
g.diff().path(path_to_file)
Can Someone please help me out? :-(

You thought in the right direction.
For this purpose use
require 'git'
g = Git.open('path/to/dir')
g.diff('your.file').patch #=> changes in your.file
For example we had empty files git.rb and smth in our git-repo.
Then we changed them and checked difference:
$ git diff
diff --git a/git.rb b/git.rb
index e69de29..3ff224d 100644
--- a/git.rb
+++ b/git.rb
## -0,0 +1,3 ##
+require 'git'
+g = Git.open(__dir__)
+puts g.diff('smth').patch
diff --git a/smth b/smth
index e69de29..7c5bd35 100644
--- a/smth
+++ b/smth
## -0,0 +1 ##
+we want to know changes
As already guessed from modified git.rb, now we will see changes only in smth:
$ ruby git.rb
diff --git a/smth b/smth
index e69de29..7c5bd35 100644
--- a/smth
+++ b/smth
## -0,0 +1 ##
+we want to know changes
In case there are no changes, you will get empty string "".

You might want to use the McIlroy-Hunt longest common subsequence (LCS) algorithm directly instead of using derivates/wrappers of it.
See https://github.com/halostatue/diff-lcs
The diff will change if you compare changes of a vs. b as opposed to changes of b vs. a, but you can run it against an array or against a whole file of course.
The gem also has the classic diff tool formatting (used by diff or git) if you prefer that instead of using its direct output.

Related

gopls replace variable in the entire project

The question born from the fact that gorename does not support modules. There is a replacement tool called gopls, but the example is not exhaustive so I'm wondering if there is way to rename a variable in the entire project (otherwise I'm failing to understand the usefulness of the rename part)
gopls rename --help says:
Usage: rename [flags]
Example:
gopls rename helper/helper.go:8:6
gopls rename helper/helper.go:#53
Is there a way to rename a variable in the entire project like gorename is able to do?
I recommend you use gopls as an LSP server to integrate with your editor. Your editor should provide an lsp-rename command to conveniently invoke the rename functionality.
As for using gopls rename on the command line, if you have this module:
--- go.mod ---
module foo
--- foo.go ---
package foo
var Foo = 123
Inside the module's directory, run gopls rename -d foo.go:3:5 Hello to preview the diff ("3:5" means line 3, column 5):
% gopls rename -d foo.go:3:5 Hello
--- /Users/muir/scratch/foo/foo.go.orig
+++ /Users/muir/scratch/foo/foo.go
## -1,3 +1,3 ##
package foo
-var Foo = 123
+var Hello = 123
Then run gopls rename -w foo.go:3:5 Hello to write out the changes. Assuming you are using a recent version of gopls, this will rename within the entire module.

report filename that is being checked

How to make pylint print filename (and maybe counter) of that it checks at the moment?
Documentation shows how to format messages if a problem is found, but not how to report progress. https://pylint.readthedocs.io/en/latest/user_guide/output.html
This currently isn't possible. It's easy to add to pylint's code:
index 258bfdc3..30bac9f1 100644
--- a/pylint/lint.py
+++ b/pylint/lint.py
## -839,6 +839,8 ## class PyLinter(config.OptionsManagerMixIn,
if not self.should_analyze_file(modname, filepath, is_argument=is_arg):
continue
+ print(modname)
+
self.set_current_module(modname, filepath)
# get the module representation
ast_node = self.get_ast(filepath, modname)
but I never got to making this a proper pull request with an option and all.

bash completion random characters

I am having trouble with bash_completion. When I expand variables, I am fine, but when I use a commands completion (such as git or vim-addon-manager), then the completion throws random characters in there. This didn't use to happen to me, I can't figure out what it is.
This is an example of what happens when I type gitTabTaby
[11:11] me#my_computer:~ $ git
Display all 131 possibilities? (y or n)
^[[01;31m^[[K c^[[m^[[Kheckout delete-tag f^[[m^[[Kmt-merge-msg i^[[m^[[Knit-db notes rm
a^[[m^[[Kdd c^[[m^[[Kheckout-index d^[[m^[[Kaemon f^[[m^[[Kor-each-ref i^[[m^[[Knstaweb obliterate setup
a^[[m^[[Klias c^[[m^[[Kheck-ref-format d^[[m^[[Kelete-branch f^[[m^[[Kormat-patch info p4 shortlog
a^[[m^[[Km c^[[m^[[Kherry d^[[m^[[Kelete-merged-branches f^[[m^[[Ksck line-summary pull show
a^[[m^[[Knnotate c^[[m^[[Kherry-pick d^[[m^[[Kelete-submodule f^[[m^[[Ksck-objects l^[[m^[[Kog pull-request show-branch
a^[[m^[[Kpply c^[[m^[[Klean d^[[m^[[Kescribe fresh-branch l^[[m^[[Ks-files push show-tree
a^[[m^[[Krchive c^[[m^[[Klone d^[[m^[[Kiff g^[[m^[[Kc l^[[m^[[Ks-remote rebase squash
a^[[m^[[Krchive-file c^[[m^[[Kolumn d^[[m^[[Kiff-files g^[[m^[[Ket-tar-commit-id l^[[m^[[Ks-tree refactor stage
b^[[m^[[Kack c^[[m^[[Kommit d^[[m^[[Kiff-index g^[[m^[[Krep local-commits reflog stash
b^[[m^[[Kisect c^[[m^[[Kommits-since d^[[m^[[Kifftool graft mergetool release status
b^[[m^[[Klame c^[[m^[[Kommit-tree d^[[m^[[Kiff-tree h^[[m^[[Kash-object m^[[m^[[Kailinfo relink submodule
b^[[m^[[Kranch c^[[m^[[Konfig effort h^[[m^[[Kelp m^[[m^[[Kailsplit remote subtree
b^[[m^[[Kug c^[[m^[[Kontrib extras h^[[m^[[Kttp-backend m^[[m^[[Kerge rename-tag summary
b^[[m^[[Kundle c^[[m^[[Kount feature h^[[m^[[Kttp-fetch m^[[m^[[Kerge-base repack tag
c^[[m^[[Kat-file c^[[m^[[Kount-objects f^[[m^[[Kast-export h^[[m^[[Kttp-push m^[[m^[[Kerge-file repl touch
c^[[m^[[Khangelog c^[[m^[[Kreate-branch f^[[m^[[Kast-import ignore m^[[m^[[Kerge-index replace undo
c^[[m^[[Kheck-attr c^[[m^[[Kredential f^[[m^[[Ketch i^[[m^[[Kmap-send m^[[m^[[Kerge-octopus request-pull whatchanged
c^[[m^[[Kheck-ignore c^[[m^[[Kredential-cache f^[[m^[[Ketch-pack i^[[m^[[Kndex-pack mv reset
c^[[m^[[Kheck-mailmap c^[[m^[[Kredential-store f^[[m^[[Kilter-branch i^[[m^[[Knit name-rev revert
Another example is vam tetris (vam tetTabTab):
^[[01;31m^[[Kaddon: tet^[[m^[[Kris
For vam install tetTabTab, it actually renders it an invalid argument (it's also quite difficult to read), so how can I fix this?
I was experiencing the same problem and saw your answer and changed:
export GREP_OPTIONS='--color=always' to export GREP_OPTIONS='--color=auto'This seems to have fixed the problem with bash-completion on my Mac.
Apparently, bash completions don't like when grep is colored. Anything like
alias grep='grep --color=always'
alias fgrep='fgrep --color=always'
alias egrep='egrep --color=always'
will give you problems.
Therefore, as Garrett Bellomy details below, it may be wise to use --color=auto, which can be achieved by setting GREP_OPTIONS (or by aliasing grep in your rc file). If you want to make this a global variable, add this to ~/.bash_profile (for bash) or ~/.zprofile (zsh) depending on your default shell: export GREP_OPTIONS='--color=auto'

RSpec - script to upgrade from 'should' to 'expect' syntax?

I have hundreds of files that also have hundreds of 'should' statements.
Is there any sort of automated way to update these files to the new syntax?
I'd like options to both create new files and also modify the existing files inline.
sed is a good tool for this.
The following will process all the files in the current directory and write them out to new files in a _spec_seded directory. This currently handle about 99%+ of the changes but might still leave you with a couple of manual changes to make (the amount will depend on your code and coding style).
As always with a sed script you should check the results, run diffs and look at the files manually. Ideally you are using git which helps make the diffs even easier.
filenum=1
find . -type f -name '*_spec.rb' | while read file; do
mkdir -p ../_spec_seded/"${file%/*}"
echo "next file...$filenum...$file"
let filenum+=1
cp "$file" ../_spec_seded/"$file"
sed -i ' # Exclude:
/^ *describe .*do/! { # -describe...do descriptions
/^ *it .*do/! { # -it...do descriptions
/^[[:blank:]]*\#/! { # -comments
/^ *def .*\.should.*/! { # -inline methods
/\.should/ {
s/\.should/)\.to/ # Change .should to .to
s/\(\S\)/expect(\1/ # Add expect( at start of line.
/\.to\( \|_not \)>\=/ s/>\=/be >\=/ # Change operators for
/\.to\( \|_not \)>[^=]/ s/>/be >/ # >, >=, <, <= and !=
/\.to\( \|_not \)<\=/ s/<\=/be <\=/
/\.to\( \|_not \)<[^=]/ s/</be </
/\.to\( \|_not \)\!\=/ s/\!\=/be \!\=/
}
/\.to +==\( +\|$\)/ s/==/eq/
/=\~/ { # Change match operator
s/=\~/match(/
s/$/ )/
s/\[ )$/\[/
}
s/[^}.to|end.to]\.to /).to / # Add paren
/eq ({.*} )/ s/ ({/ ( {/ # Add space
/to\(_\|_not_\)receive/ s/_receive/ receive/ # receive
/\.to eq \[.*\]/ {
s/ eq \[/ match_array([/
s/\]$/\])/
}
/expect.*(.*lambda.*{.*})/ { # Remove unneeded lambdas
s/( *lambda *{/{/
s/ })\.to / }\.to /
}
/expect *{ *.*(.*) *})\.to/ { # Fix extra end paren
s/})\.to/}\.to/
}
}
}
}
}' ../_spec_seded/"$file"
done
Please use with caution. Currently the script create new files in _seded/ for review first for safety. The script is placed in /spec directory and run from there.
If you have hundreds of files this could save you hours or days of work!
If you use this I recommend that "step 2" is do manually copy files from _spec_seded to spec itself and run them. I recommend that you don't just rename the whole directories. For one thing, files, such as spec_helper.rb aren't currently copied to _spec_seded.
11/18/2013 Note: I continue to upgrade this script. Covering more edge cases and also making matches more specific and also excluding more edge cases, e.g. comment lines.
P.S. The differences which should be reviewed can be seen with (from the project directory root):
diff -r /spec /_spec_seded
git also has nice diff options but I like to look before adding files to git at all.
Belated update, mainly for those who may find their way to this page via a search engine.
Use Yuji Nakayama's excellent Transpec gem for this purpose. I've used it over 10 times now on different projects without issue.
From the website:
Transpec lets you upgrade your RSpec 2 specs to RSpec 3 in no time. It supports conversions for almost all of the RSpec 3 changes, and it’s recommended by the RSpec team.
Also, you can use it on your RSpec 2 project even if you’re not going to upgrade it to RSpec 3 for now.

get the latest commit where a file changed

My task at hand is to figure out, what is the commit id of the last commit, where a specific file changed. I'm using ruby / rugged. The only solution I came up with is to walk over all commits, search for the file in the tree associated with the commit for that file and compare that files oid with the oid of the file from the first (latest) commit:
def commit_oid commit, file
commit.tree.walk( :postorder ) { | root, obj |
return obj[ :oid ] if "#{root}#{obj[ :name ]}" == file
}
raise "\'#{file}\' not found in repository"
end
def find_last_commit file
johnny = Rugged::Walker.new( get_repository )
johnny.push get_repository.head.target
oid = commit_oid johnny.first, file
old_commit = johnny.first.oid
johnny.each do | commit |
new_oid = commit_oid commit, file
return old_commit if new_oid != oid
old_commit = commit.oid
end
old_commit
end
This works but seems to be quit complicated. There must be an easier ways to get the information, "what changed with a commit". Is there an easier, more straight forward way to accomplish the same?
Running $ git log <file> will give you a reverse chronological log of only commits that altered the given file. $ git whatchanged <file> will do the same, adding a line with details of the change (i.e. mode change, change type). It's great for visual purposes, but not so much for scripting.
If you want just the hash of the most recent commit, the following will work well: $ git rev-list --max-count 1 HEAD <file>

Resources