This question already has answers here:
Git Alias - Multiple Commands and Parameters
(11 answers)
Closed 9 years ago.
I generally use these commands on branch 'work' with git
git commit -a -m "blah! blah!"
git checkout master
git merge work
git checkout work
I have heard about git aliases. Is it possible to combine all these commands into one via aliases or something else.
As well as the answers to the duplicate question posted in the comments, you can also put an executable shell script called git-foo (for example) somewhere in your PATH and access it with git foo <args> as though it were a native Git command.
This could be useful if the logic for your alias is too lengthy for a single line in your config file.
For example:
$ cat > ~/bin/git-foo
#!/bin/bash
echo "Foo: $1"
^C
$ chmod +x ~/bin/git-foo
$ git foo test
Foo: test
As #Will-Vousden suggests, it would be possible to write a shell script to do this. For example:
#!/bin/bash
git commit -a -m "$1"
git checkout master
git merge work
git checkout work
which you would call like:
$ git-commit.sh "Commit message in quotes"
However, this approach (or your own alias) could easily fail if there is, for example, a merge conflict:
$ git-commit.sh "Commit message in quotes"
[work 1a6e17f] Commit message in quotes
1 file changed, 1 insertion(+), 1 deletion(-)
Switched to branch 'master'
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.
file.txt: needs merge
error: you need to resolve your current index first
leaving you in the master branch with an unresolved merge conflict.
You could probably add some logic into the script to detect -- and potentially work round -- such issues, but that sounds like much more work and effort than just running the four separate commands!
You could also add the following to your shell.rc file:
gmm merges all given branches with the one you are currently on
invoke: gmm branch1-to-be-merged branch2-to-be-merged ...
gmm () {
src=`git branch --no-color | grep "*" | cut -f 2`
for i in $#
do
git checkout $i
git merge $src
done
git checkout $src
}
Related
I am wanting to run a shell script when I invoke the command git worktree add. Reading the docs for post-checkout seems like it would run for git worktree add but it would also run for other commands which I don't want to use it for, such as git checkout.
Is there any other hook I could use? Or perhaps I could use post checkout but have the script setup so it exits if it isn't the git worktree add command?
The reason I want to do this is to run a set of commands to set up my directory that is required when I run git worktree add, but I wouldn't need to do this setup for a normal git repository that is just using git checkout commands.
I have come up with a solution, though I am not sure how rebust it is. I use the post-checkout hook as a bash script, and I have the following lines at the top.
if ! [[ "$0" =~ ".bare/hooks/post-checkout" ]] \
|| ! [[ "$1" == "0000000000000000000000000000000000000000" ]]; then
echo "WARNING: post-checkout hook running for reason otherwise than git worktree add, bailing";
exit 0;
fi
# Do something down here every time we run `git worktree add`
How this works: $0 is the path of the script and $1 is the reference of the previous head.
By convention I have been cloning my bare repository into a directory called .bare which is what the first statement is checking. The second statement is checking that the previous ref of HEAD is that string of 0s. This catches and exits if you are just using other checkout commands such as git checkout because in that case the previous HEAD is not the string of 0s because it was pointing to a commit. However, it seems that because I create a new HEAD every time we run git worktree add it sets the previous HEAD ref to that string of 0s, which allows me to assert on that condition.
This does work for my use case so I am not sure if there is a better way or not. If anyone has a better suggestion then let me know.
P.S. I can get the directory of the new branch simply using pwd in my post-checkout script, which is useful for the commands that I want to run in the hook.
I've written a git hook to programmatically check the differences on a folder for certain commits. Unfortunately, it doesn't pass our use case because it doesn't check the contents of the subfolders within that folder; it just checks the file contents of the first folder. Is there a way for us to check the subfolders without explicitly writing git diff statements for both of them?
Our code:
dataChangedStr=$(git diff --quiet $oldrev $newrev -- config/extensions/ || echo changed)
We have two separate subfolders within config/extensions called entity and typelist that aren't checked for differences by the above statement.
You can use find to produce the list of directory paths to check for differences and invoke git diff on each of them:
find config/extensions -maxdepth 3 -type d -exec git diff --quiet $oldrev $newrev -- '{}' +
You can adjust the maximum level of recursion with the -maxdepth option.
I've written a git hook to programmatically check the differences on a folder for certain commits. Unfortunately, it doesn't pass our use case because it doesn't check the contents of the subfolders within that folder; it just checks the file contents of the first folder. ...
This is not the case.
It's worth noting here that Git doesn't actually store folders at all, but rather only files. Git does, however, understand what it calls a pathspec, in which file names behave in a "folder-structured fashion". So if commit A contains files named config/extensions/entity/one.txt and config/extensions/entity/two.txt, and commit B contains files named config/extensions/typelist/one.txt and config/extensions/typelist/two.txt, git diff A B will compare all four files.
The results of this comparison are a bit tricky, because git diff itself is a user-oriented command. This means its behavior depends on user-specific configuration, which in turn means that it's unwise to use it in any scripts in which you want predictable behavior that's not dependent on user configurations. But we can see that git diff does in fact check these things:
$ git init
Initialized empty Git repository in ...
$ mkdir config/ config/extensions/
$ mkdir config/extensions/entity
$ echo one > config/extensions/entity/one.txt
$ echo two > config/extensions/entity/two.txt
$ git add config/extensions
$ git commit -m initial
[master (root-commit) b36d1fb] initial
2 files changed, 2 insertions(+)
create mode 100644 config/extensions/entity/one.txt
create mode 100644 config/extensions/entity/two.txt
$ A=$(git rev-parse HEAD)
$ git mv config/extensions/entity config/extensions/typelist
$ git commit -m 'rename'
[master 02c48f6] rename
2 files changed, 0 insertions(+), 0 deletions(-)
rename config/extensions/{entity => typelist}/one.txt (100%)
rename config/extensions/{entity => typelist}/two.txt (100%)
$ B=$(git rev-parse HEAD)
$ git diff $A $B
diff --git a/config/extensions/entity/one.txt b/config/extensions/typelist/one.txt
similarity index 100%
rename from config/extensions/entity/one.txt
rename to config/extensions/typelist/one.txt
diff --git a/config/extensions/entity/two.txt b/config/extensions/typelist/two.txt
similarity index 100%
rename from config/extensions/entity/two.txt
rename to config/extensions/typelist/two.txt
Limiting the diff to config/extensions doesn't change the result, or the status:
$ git diff --quiet $A $B -- config/extensions || echo changed
changed
It's generally wiser to use git diff-tree here. Using git diff-tree is often a bit harder because:
it's not a command users are used to using, and
it has different defaults from git diff.
In particular, it won't by default recurse into a directory to show you exactly what changed, merely dumping out the topmost "tree" entity that implies that there is something different in some files and/or sub-trees within the tree entry. But in this case that just gives you a faster comparison, since all you want is the status:
$ git diff-tree $A $B
:040000 040000 e4532e3b5a1748d2fbc5a5a5b15525f9fd3b6817 434cdb32870dd0a387be437cc4de54df4af80ed3 M config
and:
$ git diff-tree --quiet $A $B || echo changed
changed
In all cases, even with the user-dependent behavior of git diff, you should be getting the correct result here. What's not clear to me, then, is what's actually going wrong with your test case. A Minimal Reproducible Example would help a lot here.
I have this simple function in my bashrc file
function aupgrade {
cat ~/.bash_aliases > ~/bash/.bash_aliases
cd ~/bash
git add .
if [[ $1 == "" ]]; then
git commit -m "Update"
else
git commit -m "$1"
fi
git push origin master
cd - 1>/dev/null
}
This function has a purpose, this is the expected behavior:
First, replace the content of the .bash_aliases file in the bash repository with the stdout of cat ~/.bash_aliases
Second, go into the ~/bash directory which is a git respository
Third, stage all changes
Fourth, if when calling the aupgrade function the following argument is nothing, just commit with the "Update" message, but if the user wrote an argument, like aupgrade "New commit!", commit the changes with such argument as the message, git commit -m $1
Fifth, push the changes
Sixth, go back to the previous directory
BUT, it doesnt do that, instead it just does:
First, replace the content of the .bash_aliases file in the bash repository with the stdout of cat ~/.bash_aliases
Second, go into the ~/bash directory which is a git respository
Third, stage all changes
Fourth, commit with the "Update" message although there is an argument
Fifth, push the changes
Sixth, go back to the previous directory
This is weird. This looks product of the git push origin master line. It's not a conditional problem, because when I wrote another function like this but without the git push origin master line it worked as desired.
Why does this happen? Is there any solution?
This is what set -x shows to me, it's really weird
So I have tested your code, and the only problem I could find is that passing your first argument as a string with multiple exclamation marks inserts the same (or previous?) command unless you escape it:
It looks like this is exactly what happened for you, but you had "set -x" ran beforehand, thus it replaced the double exclamation marks with that command.
I'd also recommend storing function arguments in named local variables for cleaner code.
I am trying to write a bash alias to wrap around a Git commit command.
Here is what my typical Git commit looks like.
git commit -am 'Comments in here'
Here is what I have attempted to write as an alias (which would go inside my .bashrc file), so I don't have to write out the whole command every time.
comm(){
git commit -am $1
}
Then I would call it like this: comm 'Comments in here'
However I keep getting this error: fatal: Paths with -a does not make sense.
Anything I'm missing here?
Use More Quotes™ - $1 is being split into words (which have a special meaning in *nix shell scripts). So any words except the first one in your commit message is treated as a filename, which indeed does not make sense with -a.
The -a (automatically add) option is causing the error. If you use it with a path on the git command, you get errors.
The -m option requires an argument, the message, so you either need to make your function manage two arguments or change how you use the function.
To provide a generic git-checkin-with-message function, I'd use something like this:
gcim() {
git commit -m "${1:?'Missing git commit message!'}"
}
Then use it just like git commit -m. For example:
gcim "My cool commit does really good stuff"
Adding files to the commit is almost always better done beforehand. git -a will happily add all files if you happen to use . as an argument. The only thing protecting git commit -a from inserting unwanted files is a properly constructed .gitignore file.
However, if you sometimes want to do a git -a -m "some msg" command, you can make another function:
gciam() {
local msg="${1:?Missing commit message!}"
shift
if (( $# > 0 )) ; then
git commit -m "$msg" $*
else
echo "Missing files to add!"
fi
}
Then, to use:
gciam "added new file" that_file.rb
I have a requirement to delete branches in git repository of a developer who recently left the organization. So I used git for-each-ref to list all the branches then used grep to filter results by developer name (test_developer in the following script). I used shell read and extracted branch into a variable mybr and used git push origin --delete to delete the branch. Please find the code snippet below:
git for-each-ref --format='%(align:1,left)%(color:yellow)%(authorname)%(end) %(color:reset)%(refname:strip=3)' --sort=authorname refs/remotes | grep test_developer | while read line;do mystr=(${line}); mybr=${mystr[1]}; git push origin --delete "$mybr"; done
Issue is am getting output as "fatal: remote part of refspec is not a valid name in :?[mbugfix/CRIP-2475". Here bugfix/CRIP-2475 is the branch name. And I wonder what are those extra characters :?[m that got appended before the branch name.
If I do echo $mybr before git delete I get the value properly printed as "bugfix/CRIP-2475". And if I pass this value manually to git delete, it is working fine. But when it is being passed as a variable, am getting the above error. I suspect there are some special characters being prepended, may be a ctrlM character or something that echo is not printing to the screen.
Is there anyway to remove those extra characters?
Yes you can remove by using Bash sub-string replacement
${str/#find/replace} for replace pre-fix characters
${str/%find/replace} for replace post-fix characters
git for-each-ref --format='%(align:1,left)%(color:yellow)%(authorname)%(end) %(color:reset)%(refname:strip=3)' --sort=authorname refs/remotes | grep test_developer | while read line;do mystr=(${line}); mybr=${mystr[1]}; git push origin --delete "${mybr/#?[m/}"; done
These "magical" symbols are, of course, colors. You shoudn' use colors in pipes:
git for-each-ref --format='%(align:1,left)%(authorname)%(end) %(refname:strip=3)' --sort=authorname refs/remotes | …