Splitting Repository Subfolder / Subdirectory in GIT

I had a GIT repository that I wanted to structure differently.  I have also used this this same technique for splitting up their repositories into submodules.

The quickest approach, in my mind, was to first split out the history of a folder into a new branch:

  • git subtree split -P <name-of-folder> -b <name-of-new-branch-to-store-history>

After that go and create a new repository and pull in all the history from the folder:

  • git init
  • git pull </path/of/original repo> <name-of-new-branch-created-above>

Done with that repository.

Now back in your “original” repository you may want to clean out the history of that folder OR you may not.  In one case, I left the history alone and just deleted the folder.  In another I cleaned out the history because I thought it was redundant for that particular case.

To filter out a folder in the larger/original repository:

  • git filter-branch –subdirectory-filter <folder> — –all

Hope it helps!

Branch is Currently Checked Out

We are running Gerrit and on our server we were trying to delete the ‘master’ branch since we no longer use it as the main stream.  In fact, we don’t want people pushing to it so we decided to delete it.  Then we got the following:

To ssh://git:29418/MyRepo
! [remote rejected] master (branch is currently checked out)
error: failed to push some refs to ‘ssh://git:29418/MyRepo’

The branch is currently checked out!?  It doesn’t sound like a gerrit failure.  Must be Git.

Thinking through, it’s a bare repository – how could the branch be checked out.  Then I logged onto the machine and did ‘git branch’:

* master
otherBranch

Sure enough the master is set as the HEAD.

This means we need to update HEAD to another branch.  Running the following command is necessary:

git symbolic-ref HEAD refs/heads/otherBranch

Now we look after running ‘git branch’

master
* otherBranch

Perfect!  The symbolic-ref command successfully updated the bare repo to be looking at another branch as it’s HEAD!

Hope that helps someone.

fatal: C:\Program Files\Git/libexec/git-core/git-pull cannot be used without a working tree.

You might also see: git-pull cannot be used without a working tree

When executing a pull you see something like this:

1
fatal: C:\Program Files\Git/libexec/git-core/git-pull cannot be used without a working tree.

To Fix:

Check under:

1
\.git\config

Under the [CORE]

1
2
3
4
5
6
7
8
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
autocrlf = false
worktree = &lt;&lt;&lt;&lt;&lt;&lt; DELETE THIS LINE!!

Change GIT Editor

By default the git commands that require an editor would use ‘vi’, but if you can add an entry into your global git config to change the editor to Notepad++. To change to some other editor such as Notepad++ or Textpad you can add these lines to the global git config.  For example:

[core]
   editor = '/C/Program Files (x86)/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin

OR

[core]
   editor = '/C/Program Files (x86)/TextPad 5/TextPad.exe' -m

Either edit the global config manually or you can run this git command

git config --global core.editor '/D/ProgramFiles/Notepad++/notepad++.exe -multiInst -notabbar -nosession -noPlugin'

NOTE: If your editor path contains spaces you could have trouble running the above git command. Recommend to just edit the global config directly in that case to ensure the editor gets added correctly.

Using Beyond Compare in QGIT

You can configure these tools to do diffs using Beyond Compare (or some other favorite diff tool)

  • Open QGIT
    • Either – right-click on a folder in the repo and select “Git GUI Here”
    • Or – launch QGIT.exe and choose the repo folder
  • Go to Edit->Settings…->General tab->External diff tool. You cannot have spaces in the path, so you’ll need to enter it manually in a format like:
1
2
"C:\PROGRA~2\BEYOND~1\BC2.exe" //c:/Program Files(x86)/Beyond Compare/BC2.exe - Windows 7
"C:\PROGRA~1\BEYOND~1\BC2.exe" //c:/Program Files/Beyond Compare/BC2.exe - older Windows

Git and Submodules

We were looking at using submodules to store all of Windchill code. Currently it is around 10 million lines of code in over 400+ modules. It seems to make sense to have every module a git repo even though some modules are quite small.

I was curious though if when using git submodules we could still have a quick response to the question, “What did I change?”. If you ran “git status” in the super module it responds rather quickly with the modified submodules. Unfortunately it does not respond with the names of the files changed. It wouldn’t be performant to do a git status in each submodule, especially when you know right away which submodules changed. So that is where this script comes in handy.

1
alias cdv="cd /f/Git_Module/git_super_module"
1
2
3
4
5
username@localhost /f/Git_Module/git_super_module (master)
$ function sstat { cdv; for m in $( git status -s | grep -E '^ M' | cut -c4- );
do if [ -e $m/.git ]; then cd $m; ( git diff --name-status HEAD; git ls-files
-o --exclude-standard | sed -e 's/^/A /' ) | sed -e "s,\s, $m/,"; cdv; else ech
o $m; fi; done; }
1
2
3
username@localhost /f/Git_Module/git_super_module (master)
$ sstat
M Folder/Where/File/src/loadFiles/customization/fileModified.xml

Awesome! So now we can see which files were modified in a few seconds (on linux this is less then a second). That is pretty sweet.

Until next time.

Git and Notepad++

Simple but really helpful if you want to use Notepad++ as your default editor when using GIT. The location will depend upon your specific location. I happen to be on Windows 7 x64 bit.

1
2
[core]
    editor = \"/C/Program Files (x86)/Notepad++/notepad++.exe\" -m

New Git Repository from an Existing Subdirectory

In this situation, I had been working in a git repository which had become to large and needed to be split into separate sub-modules. In this case, I really just needed to take out a subdirectory and make it a brand new repository. The great thing is that you can preserve all the history that had happened for that subdirectory if you decide that you want to keep it. There are a number of ways to do this process so this is just the way that I decided and liked the best.

Create new repository from existing subdirectory
First step I did was to make a clone of my existing repository (either by doing a regular copy/paste or a git clone).

Now by doing a filter-branch command we can use the existing git repository starting at the level of the subdirectory:

1
git filter-branch --subdirectory-filter foodir -- --all

After this is complete I needed to clean up and compact the repository:

1
2
3
git clean -d -f  //clean up untracked files
git gc --aggressive //clean up the repository packing
git prune

Done!

Remove history from existing full repository
This is optional if you want to re-write history from your existing repository. Some might just want to do a remove on the directory and then commit. I wanted to go back and re-write history to remove the commits to the subdirectory.

Then I did another clone of the repository which will clean from the history the subdirectory since I didn’t want that information to be in the repository anymore.

1
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch dir' -- --all

Run a git gc again in this repository just to clean up everything.

Client Side Git Hook

A client side Git hook that I wrote and are using at work. Thought others might have a similar need. Basically it will check the selenium file for the owners and see if our group is in that ownership. If the file is not in our group a message is displayed saying that the owner will need to get permissions and code review by the owners.

Of course you can use any language you want to execute your client side git hook. I thought shell scripting would just be easiest in my case since it wasn’t going to be that much code and anyone that has git will either be running in windows with cygwin and thus of bash or they will be in Linux.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/bin/bash
#
# This hook is meant to do a pre-check to see if we have
# files outside of common components that need dac
# permissions
#
# Contact: Ryan Alberts
#
#############################################################

#Get the modified files
files_modified=`git diff --cached --name-only`

#echo "The present working directory is `pwd`"

#false or 0 if we do not need dac permissions
need_dac=0

#Commit message
commit_msg=`cat $1`

if [[ $commit_msg == *DAC* ]]
then
    exit 0
fi

#Go through the files in the submission
for file in ${files_modified[@]}
do
    accepted_path=0
   
    check=`grep -s wc-caIntegSprintTester $file`
   
    #Check if it is apart of our package
    if [[ $check == *wc-caIntegSprintTester* ]]
    then
        accepted_path=1
    fi

    if [ $accepted_path -eq 0 ]
    then
        echo "File is owned by another team, and may require DAC permissions!"
        echo "  File: $file"
        need_dac=1
    fi
   
done

#Exit Code For Commit Message Warning Or Not
if [ "$need_dac" -eq "1" ]
then
    echo ""
    echo ">>> Notice <<<"
    echo "Please contact the branch master and work on requesting DAC permissions for the files listed above."
    echo "You can avoid seeing this message by adding 'DAC' anywhere within the commit message."
    echo ""

    exit 1
fi

#Everything looks good - allow them to commit.
exit 0