git diff --numstat by strings instead of file paths - linux

I'm trying to run git diff --no-index --numstat <string> <string> on Linux (Debian Docker container), but I'm failing in finding a way to achieve this. In essence I want to pass the files' contents as strings instead of their file paths. The goal is to retrieve the stats from the --numstat flag.
This command should be executable outside of a git repository/directory and on Linux. So far, I've found two solutions which lack the former requirements:
git diff --no-index --numstat /dev/fd/3 /dev/fd/4 3<<<$(echo "<string>") 4<<<$(echo "<string>"): This works on MacOS, but fails to work on Linux.
git diff --numstat $(echo <string> | git hash-object -w --stdin) $(echo <string> | git hash-object -w --stdin): which only works inside git repositories (got this partial solution from here)
Certainly there must be a way to achieve this, either via some git command or other bash concepts I'm unaware of. Any help would be great.
Thanks!

The reason that solution 1. isn't working is that /dev/fd/3 and /dev/fd/4 are symlinks and git diff does not follow symlinks but instead uses their link target string as their "content".
The only way to pass a string to git diff directly instead of a file is as stdin - which obviously only works for one of the files. So I see only two possible solutions to your problem:
write the strings to (temporary) files first, then pass them to git diff
use another tool, as suggested by #B--rian in the comment
Another, shorter version of 1. using process substitution would be:
git diff --no-index --numstat <(echo "<string1>") <(echo "<string2>")
Which unfortunately doesn't work either for the same reason/because git diff does not support process substitution, see https://stackoverflow.com/a/49636553/11932806

Related

How to source the files to be added to a git repo from a file?

I want to always add the same files to my git repo, and I thought that having a file of files to add to git would be an easy way to do that.
How can I ask git add to read the files to be added from a file?
It is also easy to use standard cli tools to do this :
# bash:
git add $(cat file)
# xargs is standard on linux, and comes with git-bash on Windows :
cat file | xargs git add
It seems that git add --pathspec-from-file=file is just what I was looking for.
Just make sure that all lines are valid file names. And that none are empty.

What do the commands with this '-' sign do?

I have this command:
git checkout -b <name>
What does -b do in this command? Where can I read about such commands in git and in the terminal in particular?
The -b option specifies a git branch to check out.
For more information, view the git documentation.
The description of the -b option in the git documentation is a little dense:
git checkout -b|-B <new_branch> [<start point>]
Specifying -b causes a new branch to be created as if
git-branch[1] were called and then checked out. In this case you can
use the --track or --no-track options, which will be passed to git
branch. As a convenience, --track without -b implies branch
creation; see the description of --track below.
If -B is given, <new_branch> is created if it doesn’t exist;
otherwise, it is reset. This is the transactional equivalent of
$ git branch -f <branch> [<start point>]
$ git checkout <branch>
that is to say, the branch is not reset/created unless "git checkout"
is successful.
They are command options or parameters.
Commands can take many different options as input, and usually (but not always) these options are prefixed by - or --, followed by a letter or word, and then sometimes followed again by a value for that option.
For git checkout the -b option allows you to specify a value for branch name.
You can type git --help to view high level options or git checkout -h to find out about options specific to checkout function. However, Git being a large complex tool, has many many options so suggest to check out the official documentation online, rather than only the built in help on terminal.
Getting help for terminal commands in general: For most commands, you can type <command> --help or try -h if that didn't work. To read the long-form manual for a command type man <command>. To search through a list of all available commands try apropros <search terms> to find one you want.
BONUS TIP: If you are new to Linux terminal in general, and want to learn various commands quickly without having to google a lot, may I suggest installing the tldr tool.
sudo apt install tldr
Once installed via the above command, you can run tldr <command name>.
For example try tldr tar and it gives you some nice examples about how to use the tool.

git diff for *.cpp files prints nothing in one repository

Once the command git diff *.cpp just stopped printing anything in one repository, despite git diff *.h works fine. git diff works fine for *.cpp files.
What could it be?
The fix is to be careful to run, e.g.:
git diff '*.cpp'
or:
git diff \*.cpp
or more formally:
git diff -- '*.cpp'
or similar.
What's going on here?
Missing from the question: the desired output was a diff for subdir/subfile.cpp, but the top level of the work-tree contained one file named file.cpp or similar.
The problem stems from the fact that you're using a Unix/Linux-style shell, which expands * and other wildcard or glob characters before running the command you enter at the command line. But there is a bit of subtlety here as well.
Because there exists a file named file.cpp in the current directory, when you run:
git diff *.cpp
the shell replaces *.cpp with the names of all the files whose name ends with .cpp, and therefore runs:
git diff file.cpp
Git then dutifully produces the diff—or in this case, no diff since there is no difference—for the one file named file.cpp.
When there are no files named file.cpp or zorg.cpp or similar in the top level directory, however, this shell simply invokes git with arguments diff and *.cpp, as if you had quoted the asterisk. This gives Git the chance to expand the *.cpp argument, and when Git expands it, it does so in a different way than the shell.
Why use --?
The git diff command takes a number of options, such as -s, -p, -w, --name-status, --name-only, and so on.
Depending on what files you have, suppose you want a diff listing for the file named -z in the current directory. If you then run:
git diff -z
Git thinks you mean to supply the -z option, rather than to get a diff listing for the file named -z. A similar problem applies if you want diffs for -z* and the like.
In general, you can work around this problem by using the file name ./-z instead of just -z. Since ./-z does not start with -, Git is not fooled into thinking it's an option. But this problem is more general, and strikes in other cases (other commands) as well. For instance, suppose you have a file named develop and you run:
git checkout develop
Git will think you mean to check out the branch named develop.
All Git commands accept -- as a separator, generally meaning there no more options: anything after this point is an argument instead. For git diff, anything after -- is treated as a pathspec, which includes doing glob expansion, provided the glob characters made it past the shell.
This is what the syntax description in the SYNOPSIS section of the git diff documentation means:
git diff [<options>] [<commit>] [--] [<path>...]
git diff [<options>] --cached [<commit>] [--] [<path>...]
git diff [<options>] <commit> <commit> [--] [<path>...]
git diff [<options>] <blob> <blob>
git diff [<options>] --no-index [--] <path> <path>
The square brackets indicate that something is optional, so all the options are optional. The angle brackets indicate that some argument should be replaced with a string that meets the requirements of the type inside the brackets. The literal -- or other literal options imply that you should type those characters literally—so git diff --cached requires the literal string --cached, for instance. Last, the ... means "repeat the previous as often as you like".
Since the literal string -- is optional, you don't have to enter it—but if you do, everything after it must have the form of a <path>. That form is quite general: almost any character is valid. The documentation is missing a cross-reference to the definition of a pathspec, though (and probably should use <pathspec>, not just <path>, here). The full description of pathspecs is in the gitglossary.
So I have one .cpp file in the repository root directory, and so git diff *.cpp now prints changes only for this file, while git diff '*.cpp' works fine for all files in subdirectories.

Why isn't git reading the added file?

I was reading an article that told me to add a file and place it in my path. Not knowing what the author meant by path, i simply put it in my root directory.
Trying to run 'git diffall', git says diffall is not a command, any ideas? Thanks in advance.
The article snippet for more information:
Write the following code to a file called git-diffall and place in your path (I put it in >…/my-git-install-dir/cmd/ )
#!/bin/sh
git diff --name-only "$#" | while read filename; do
git difftool "$#" --no-prompt "$filename" &
done
And run it in git (with usual diff input parameters), for example:
git diffall
git diffall HEAD
your 'path' is the collection of directories where the system looks for executables. To see it, simply execute echo $PATH at the commandline. Then put your script in one of those directories.

How do you pass a file list to the linux zip command

I'm using Git for version control and unlike SVN I have not come across an inherent means of performing an export of changed files between 2 revisions, branches or tags.
As an alternative I want to use the linux zip command and pass it a set of file names, however the file names are the result of another command git diff. Below is an example of what I am trying to achieve:
zip /home/myhome/releases/files.zip git diff --name-only -a 01-tag 00-tag
However the above does not work as I guess the 'zip' command sees the git operation as part of its command options.
Does someone know how I can make something like the above work?
Thanks
You need to execute the git command in a sub-shell:
zip /home/myhome/releases/files.zip `git diff --name-only -a 01-tag 00-tag`
# another syntax (assuming bash):
zip /home/myhome/releases/files.zip $(git diff --name-only -a 01-tag 00-tag)
Another option is the xargs command:
git diff --name-only -a 01-tag 00-tag | xargs zip /home/myhome/releases/files.zip
If you're in a git shell (bash) you can also do this:
git diff -–name-only commit1 commit2 | zip ../Changes.zip –#
Works for me on Windows and Unix based systems.

Resources