change directory in bash is not affecting the post-commit hook - bash

i've made the following bash script to commit the parent repo after some change in submodule.
it's all about that the script want to cd .. to check the parent repo current branch but the problem is that the cd .. is not affecting the upcoming commands because i guess the subshell
i've tried to run
1- cd ../ && before each command
2- make alias but didn't succeed
3- run exec but the script didn't continued
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "post-commit".
commit_msg= git log -1 --pretty=%B
if [[ $(git branch | grep \* | cut -d ' ' -f2) == "int1177/next" ]]; then
cd ..
if [[ $(git branch | grep \* | cut -d ' ' -f2) == "B0/next" ]]; then
git add 6_Tests
git commit -m "bs esss"
echo "development branch B0/next has now new commit"
else
echo "development branch isn't B0/next"
fi
else
echo "current branch isn't int1177/next"
fi

Actually, this particular problem is not a bash issue, but rather a Git issue.
Why doesn't "cd" work in a shell script? is valid in general, and is a suitable answer to many other questions. But this particular post-commit hook is trying to chdir out of a submodule into its parent superproject, then make a commit within the parent superproject. That is possible. It may be a bad idea for other reasons—in general it's unwise to have Git commit hooks create commits, even in other repositories1—but in this particular case you're running into the fact that Git finds its directories through environment variables.
In particular, there's an environment variable GIT_DIR that tells Git: The .git directory containing the repository is at this path. When Git runs a Git hook, Git typically sets $GIT_DIR to . or .git. If $GIT_DIR is not set, Git will find the .git directory by means of a directory-tree search, but if $GIT_DIR is set, Git assumes that $GIT_DIR is set correctly.
The solution is to unset GIT_DIR:
unset GIT_DIR
cd ..
The rest of the sub-shell commands will run in the one-step-up directory, and now that $GIT_DIR is no longer set, Git will search the superproject's work-tree for the .git directory for the superproject.
As an aside, this:
$(git branch | grep \* | cut -d ' ' -f2)
is a clumsy way to get the name of the current branch. Use:
git rev-parse --abbrev-ref HEAD
instead, here. (The other option is git symbolic-ref --short HEAD but that fails noisily with a detached HEAD, while you probably want the quiet result to be just the word HEAD, which the rev-parse method will produce.)
1The main danger in this case is that the superproject repository is not necessarily in any shape to handle a commit right now. Edit: or, as discovered in this comment, is not even set up to be a superproject for that submodule, yet, much less to have a submodule-updating commit added.

Related

How to add a file change to the latest commits of all branches in a git repo? [duplicate]

I have a repo that has over 300 branches, and I am wanting to store various files on in a Git LFS. To do this I need to add the .gitattributes file to the branches first. However, as I have over 300 branches, manually doing this will be very time consuming.
Is there a way that I can add a prepopulated .gitattributes file to the root of every branch automatically and push them?
A one-liner which assumes you have a branch named feature/add-gitattributes which makes the necessary changes;
git for-each-ref refs/remotes/origin --format="%(refname:lstrip=3)" | xargs -n 1 sh -c 'git checkout "$1"; git merge feature/add-gitattributes;' --
To break it down...
This part just gets a list of those 300 branch names;
git for-each-ref refs/remotes/origin --format="%(refname:lstrip=3)"
This part takes those names and passes them to a sub-shell;
| xargs -n 1 sh -c
This part is the command to the sub-shell which checks out the target branch and merges your feature branch to add the .gitattributes file.
'git checkout "$1"; git merge feature/add-gitattributes;' --
The trailing -- ensures the branch name is passed as an argument to the sub-shell.

git - how to get $(git_current_branch) variable from outside the git directory

I have a bash script containing the following code:
git -C "$1" push origin "$(git_current_branch)"
where "$1" is the git dir I am trying to push.
The problem is, I cannot get the correct "$(git_current_branch)" variable when I run the script from outside the git directory. I know I can just cd to "$1" before pushing, but I want a better solution (which makes my script cleaner).
Is there any way to do that? Thanks in advance!
Edit: I prefer to do this through git command option, if possible.
You can try that:
current_git_branch=$(git -C "$1" branch | sed '/^\*/!d;s/\* //')
explanation
call git in directory "$1" and print all branches
delete all branches without * at start of line
remove '* ' marker for current branch from line

Automate post-"git move", making history log stick

I am facing merging of a few repositories in to one, with miscellaneous file moved around
Based on some research on SO, SO, how to merge repositories I ended up with following sketch:
user=some_user
new_superproj=new_proj # new repository, will include old repositories
hosting=bitbucket.org # gitgub etc
r1=repo1 # repo 1 to merge
r2=repo2
...
# clone to the new place. These are throw-away (!!!) directory
git clone git#${hosting}:${some_user}/${r1}.git
git clone git#${hosting}:${some_user}/${r2}.git
...
mkdir ${new_superproj} && cd ${new_superproj}
# dummy commit so we can merge
git init
dir > deleteme.txt
git add .
git commit -m "Initial dummy commit"
git rm ./deleteme.txt
git commit -m "Clean up initial file"
# repeat for all source repositories
repo=${r1}
pushd .
cd ../${repo}
# In the throw-away repository, move to the subfolder and rewrite log
git filter-branch --index-filter '
git ls-files -s |
sed "s,\t,&'"${repo}"'/," |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD
popd
# now bring data in to the new repository
git remote add -f ${repo} ../${repo}
git merge --allow-unrelated-histories ${repo}/master -m "Merging repo ${repo} in"
# remove remote to throw-away repo
git remote rm ${repo}
So far so good, unless we want to move files around while still preserving log. Git is sucks on move/rename and log rewrite fragment is not quite adapted, hence rewrite done uniform way, recursively for whole directory
Idea is, while files are moving we know there are no other changes in repository but renames and moves. So, how can I rewrite following part to be canonical, per file. Taken from git filter-branch, official documentation
git filter-branch --index-filter \
'git ls-files -s | sed "s-\t\"*-&newsubdir/-" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
git update-index --index-info &&
mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
I have hard time to understand stuff past 'sed' and how it is applied for git filter-branch
I want to run script (bash, python etc), so:
for each file in repository get moved/renamed
...
# in the loop, moved/renamed file found
old_file="..." # e.g. a/b/c/old_name.txt
new_file="..." # e.g. a/b/f/g/new_name.txt, at this point it is known, old_file and new_file is the same file
update_log_paths(old_file, new_file) # <--- this part is needed
Any ideas?
As it turned out to be, hinting from the following command Move file-by-file in git, it is as simple as (pseudocode):
move_files
cd repo_root
git add . # so changes detected as moves, vs added/deleted
repo_moves=collect_moves_data()
git reset HEAD && git checkout . && git clean -df . # undo all moves
Biggest misunderstanding I found is "git log --follow" or other, "stronger" options doesn't work for many in related SO questions:
git log --follow <file>
does not show log until moved, while unchanged, file is committed.
for each_move in repo_moves
old_file, new_file=deduct_old_new_name(each_move)
new_dir=${new_file%/*}
filter="$filter \n\
if [ -e \"${old_file}\" ]; then \n\
echo \n\
if [ ! -e \"${new_dir}\" ]; then \n\
mkdir --parents \"${new_dir}\" && echo \n\
fi \n\
mv \"${old_file}\" \"${new_file}\" \n\
fi \n\
"
git filter-branch -f --index-filter "`echo -e $filter`"
If you need to get back:
git pull # with merge
git reset --hard <hash> # get hash of your origin/master, orignin/HEAD), which will be HEAD~2, but I'd check it manually and copy/paste hash

How can I get git's `.git` path from git itself?

I am trying to write a shell script that needs to be able to find the .git folder for the current directory, correctly handling all of the following possibilities:
I might be in a bare repo, in which case the .git folder is either . or .. or ../.. or so on.
I might be in a submodule (in which I'll find a .git file that contains the path to the git folder)
$GIT_DIR might be set.
I might not be in a git repo at all
I have this:
seemsToBeGitdir() {
# Nothing special about "config --local -l" here, it's just a git
# command that errors out if the `--git-dir` argument is wrong.
git --git-dir "$1" config --local -l >/dev/null 2>/dev/null
return $?
}
gitdir() {
local cursor relpath
if [ "$GIT_DIR" ]; then
echo "$GIT_DIR"
return 0
fi
cursor="$(pwd)"
while [ -e "$cursor" ] && ! seemsToBeGitdir "$cursor"; do
# Git won't traverse mountpoints looking for .git
if mountpoint -q "$cursor"; then
return 1
fi
# We might be in a submodule
if [ -f "$cursor/.git" ]; then
# If .git is a file, its syntax is "gitdir: " followed by a
# relative path.
relpath="$(awk '/^gitdir:/{print$2}' "$cursor/.git")"
# convert the relative path to an absolute path.
cursor="$(readlink -f "$cursor/$relpath")"
continue
fi
if seemsToBeGitdir "$cursor/.git"; then
echo "$cursor/.git"
return 0
fi
cursor="$(dirname "$cursor")"
done
echo "$cursor"
}
And it works, but seems way too complicated -- clearly, git itself does this sort of calculation every time it's invoked. Is there a way to make git itself tell me where .git is?
Use git rev-parse, which has options specifically for this:
git rev-parse --git-dir
See also:
git rev-parse --absolute-git-dir
(new in Git version 2.13.0), and:
git rev-parse --show-toplevel
and:
git rev-parse --show-cdup
(note that its output is empty if you are already in the top level of the repository). View your own documentation to find out which options your Git supports; most of these have been around since Git 1.7, though.

Outputting dirty state of git repo in command line issue

if i run this in command line with in git repo
git status 2> /dev/null | grep -iw 'modified'
the output i get
# modified: .rvmrc
so my assumption is that if i plug this in into if statement i will get true that will execute the line of code
but when i created function as part of the .bash_profile, and here is the code i have:
## Prompt ##
path="$white\w$reset"
folder_name="$white\W$reset"
git_branch="\$(git branch 2>/dev/null | grep -e '\* ' | sed 's/^..\(.*\)/$red(\1)$reset/')"
rvm="$green(\$(~/.rvm/bin/rvm-prompt i v p))$reset"
# Checks if working tree is dirty
function _git_st {
if [[ $(git status 2> /dev/null | grep -iw 'modified') ]]; then
echo "[x]"
fi
}
git_st="$yellow`_git_st`$reset"
PS1="$rvm $folder_name $git_branch $git_st \$ "
X is not echoed.... i am bit lost, and not sure what i am doing wrong.
this is the out put i get:
(ruby-1.9.2-p290) folder-git (master) $ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: .rvmrc
#
no changes added to commit (use "git add" and/or "git commit -a")
(ruby-1.9.2-p290) folder-git (master) $
The reason for this is because you are using a variable git_st to set PS1 which will be evaluated at the time of setting PS1. What you can try is to invoke the function _git_st be called instead of using git_st i.e. try something on the lines of:
PS1="$rvm $folder_name $git_branch \$(_git_st) \$ ".
Also you might be interested to know that newer versions of git provide functions along with bash completion which provide such utilities. You can take a look at __git_ps1 function if it is available.
Hope this helps!
P.S.: Here is SO post which might provide some pointers for using __git_ps1. This is the quick search result from Google.

Resources