howto find out which git submodule current directory belongs to - bash

setup
i have a git repo located in /home/v/git_repo, in which i have a submodule localted in subdirectory ./a/b/c.
$ cat /home/v/git_repo/.gitmodules
[submodule "foo/bar"]
path = a/b/c
url = git#github.com:username/repo.git
having the full path or only the in-repository subpath (that i have implemented in helper script git-where-in-repo-am-i-currently)
$ pwd
/home/v/git_repo/a/b/c/something
$ git where-in-repo-am-i-currently
a/b/c/something
question
i want to find out (preferably in fish) which submodule this path belongs to: e.g
$ git which-submodule (pwd)
foo/bar
to later use it to query that submodules status like
$ git -C (git rev-parse --git-dir)/modules/(git which-submodule) status
on branch master
Your branch is up to date with 'origin/master'
and ultimately display this information in my prompt (that part is already implemented)
what i tried
parsing the output of
$ git -C (git rev-parse --show-toplevel) config --file=.gitmodules --get-regexp "path"`
submodule.foo/bar.path a/b/c
and comparing my sub-directory path to that of a submodule, but it was rather a mess, with splitting pathes into arrays and all kinds of hacks

For the usual setup you've described here, with the worktree nesting matching the submodule nesting, you can
mytoplevel=`git rev-parse --show-toplevel`
abovethat=`git -C "$mytoplevel"/.. rev-parse --show-toplevel`
Then,
echo ${mytoplevel#$abovethat/}
will get you the submodule path in the superproject, or you can
echo ${PWD#$abovethat/}
to get your current directory's path relative to the superproject.
So:
me=`git rev-parse --show-toplevel`
up=`git -C "$me"/.. rev-parse --show-toplevel`
subpath=${me#$up/}
git -C "$up" config -f .gitmodules --get-regexp '^submodule\..*\.path$' ^$subpath$
gets you the current repo's submodule name and path from the config entry in its superproject.
Git can be useful in any imaginable build system, though; it doesn't impose restrictions on how things outside its remit are set up. So short of an exhaustive search of the filesystem namespace you can't be sure you've found everybody using any worktree as a submodule checkout, there's just no reason for Git to care how a repository is used.
For instance, if multiple projects all need to run off the same submodule rev, you can have a single repo and worktree serve as a shared submodule for them all: rather than have to go through every single one of them and do synchronized checkouts, and then trusting that you haven't missed one, just use one repo, with one worktree, and point everybody using it at that.
For workflows with that need, this can be compellingly better than the usual setup, all users by definition see a synchronized, current submodule revision and any client who needs to know "what's new" with an update can e.g. git -C utils diff `git rev-parse :utils` HEAD, every submodule user effectively has their own tracking branch and can use all of Git's tools to help stay current or resolve conflicts.
So, to recreate your setup, I do:
git init git_repo; cd $_
mkdir a/b; git init a/b/c; cd $_
mkdir something; touch something/somefile;
git add .; git commit -m-
cd `git -C .. rev-parse --show-toplevel`
git submodule add --name foo/bar ./a/b/c -- a/b/c
git add .; git commit -m-
Then I get this when I try it:
$ find -print -name .git -prune
.
./a
./a/b
./a/b/c
./a/b/c/something
./a/b/c/something/somefile
./a/b/c/.git
./.gitmodules
./.git
$ git grl
core.repositoryformatversion 0
core.filemode true
core.bare false
core.logallrefupdates true
submodule.foo/bar.url /home/jthill/src/snips/git_repo/a/b/c
submodule.foo/bar.active true
$ cd a/b/c/something
$ me=`git rev-parse --show-toplevel`
$ up=`git -C "$me"/.. rev-parse --show-toplevel`
$ subpath=${me#$up/}
$ git -C "$up" config -f .gitmodules --get-regexp '^submodule\..*\.path$' ^$subpath$
submodule.foo/bar.path a/b/c
$ echo $me $up $subpath
/home/jthill/src/snips/git_repo/a/b/c /home/jthill/src/snips/git_repo a/b/c
If there's a difference between this setup and what you've described, I'm missing it, I've got the directory structure, the submodule name, the start directory... if you'll step through that and find where the setup or results diverge from yours I think that'd help.

Related

Git short command for checking out, pulling, prune fetching and deleting

Is there a shortcut for:
Checking out on master
Pulling from master
Prune Fetch (check which remote branches are removed)
Delete those local branches
Scenario:
Let's say I'm on master and I checkout on the branch foo, I do some commits and publish foo to remote and push to it as well. Next I merge that branch to master on GitHub and delete the online version of foo since it's complete. Now in the offline environment, I have to do the following:
$ git checkout master
$ git pull
$ git fetch -p
$ git branch -d foo
or shorthand:
git checkout master && git pull && git fetch -p && git branch -D foo
Is there a command I can execute to make this much shorter? Like
$ git complete foo
or something along those lines..?
aliases can be used for solving this problem.
An alias can be created by running:
$ alias cpfb="git checkout master && git pull && git fetch -p && git branch -D"
Now, you can execute
$ cpfb foo
which will execute those commands specified in the alias.
Setting alias through terminal lasts for only that particular terminal instance.
Hence, save them in ~/.bashrc to make the alias permanent.

Is there a way to add multi steps to a git alias?

I'm going to create an alias that does this:
I've added it to my git-bash .bash_profile but I'd like to see if there is a way to add it as an alias so I don't have to use git bash
Git finish will push to current branch # Eg. gf "commit message"
gf() {
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
git add . && git commit -m "$1" && git push origin "$CURRENT_BRANCH"
} # Git merge # Eg. gm branch-name
gm() {
git merge "$1"
} # Git checkout # Eg. gc branch-name
gc(){
git checkout "$1" && gp
}
You could create a "Git-Subcommand" the name of this executable must be in this Format: git-{{ Name }} eg: git-acommit. This File must be located in one the Directories listed in $PATH, you can now execute git acommit and git will search for it in you $PATH.
To use this than in an alias simply run git config --global alias.gf acommit and you are finished. Please note: the alias step is unnecessary since you can name your file how you want, so you could also name it git-gf instead of git-acommit.
But without creating a separated file it's impossible to stack commands in a git alias.
Or you could use GitAlias to create an alias that executes a function or multiple Commands.
For an example check out the Recovery examples Section.

create git pull -f multi-token alias

I'd like to make an alias like this:
'git pull -f'='MYBRANCH="$(git rev-parse --abbrev-ref HEAD)"; echo "origin/${MYBRANCH}" | git reset --hard'
In simpler terms, it's a git reset --hard origin/my_current_branch.
But bash tells me that git pull -f cannot be a valid alias (probably because there are spaces in it?). Is there a way to get around this?
If I understand correctly, you want to perform the following steps with the alias:
Get the name of your current branch
Fetch the corresponding branch from origin
Hard-reset to the remote branch
Instead of a Bash alias to do this,
I suggest to define a Git alias.
To do that, add this in the [alias] section in your ~/.gitconfig:
myreset = "!f() { br=$(git rev-parse --abbrev-ref HEAD); git fetch origin $br; git reset --hard origin/$br; }; f"
This way, you can run git myreset to perform the operations.
(Feel free to rename "myreset" to whatever you prefer.)

How to reset the entire Git repository, and not only the "Master" branch, to match Remote?

What the title says.
I want to reset each and every local branch to match my Remote repository, including deleting some branches and tags which only exists locally, without having to delete everything and cloning from scratch. All I could find are instructions on how to reset a specific branch, but not the entire repository.
Even better if it can be done from the TortoiseGit Shell extension. But I'm also fine with the command line.
You can do it by following commands:
git checkout --orphan #
git fetch <Remote> refs/*:refs/* --refmap= --prune --force
where <Remote> is remote repository you want to use. You simply fetch all remote refs (refs/*:refs/*) with --prune and --force flags to remove and force update local references.
The following line will reset all local branches that have a configured upstream branch to the state of the upstream branch
git checkout #{0} && git for-each-ref refs/heads --format '%(refname:strip=2)' | xargs -ri sh -c 'git rev-parse {}#{u} >/dev/null 2>&1 && git branch -f {} $(git rev-parse {}#{u})'
You will end up with a detached HEAD due to the first command, because you cannot reset the currently checked out branch, so checkout the branch you want to have in your working directory after doing this.

Git should I create repo file in project folder?

I'm little bit confused when I setup Git. I have local Repository at my C:\Repository and root my project is located at C:\Projects. I want to ask should I create repo file into work project folder or not to be applied to commit and push to local repo?
If I get it right, you have a project in C:/Projects and a repository in c:/Repository and you want to push changes from the project repository into the c:/Repository.
Here how you can setup all this (I assume, you use Git for the Windows and not Cygwin git):
$ mkdir -p /c/Repository/MyProject.git
$ cd /c/Repository/MyProject.git
$ git init --bar # Create "server" (bar) repository
$ mkdir -p /c/Projects/MyProject
$ git init # Create project repository
$ git config user.name "My Name"
$ git config user.email "My#e-mail"
$ # set more properties...
$ # create some files
$ # ...
$ git add -A . # Add new files to the index
$ git commit -m "Initial commit"
$ git remote add origin /c/Projects/MyProject
$ git push origin master:master

Resources