The Linux Page

Creating a new git repository

A treasure chest full of coins, jewels, and ornaments.

Become a pro in no timeIntroduction

As I am now using git, I like to create my own repositories. In most cases the documentation is not that easy to follow. Here are the instructions I use depending on the type of repository I want to save my work in.

Personal Setup

First of all, I setup my own personal configuration (it can be used as a global setup for you). This is written in the ~/.getconfig and looks like this:

[user]
    name = AlexisWilke
    email = me@my-top-secret-email-address.com
[core]
    excludesfile = ~/.gitignore
    editor = vim
[push]
    default = simple
[giggle]
    main-window-maximized = false
    main-window-geometry = 1114x789+144+184
    file-view-vpane-position = 389
    main-window-view = FileView
    history-view-vpane-position = 315
    file-view-hpane-position = 190
[branch]
    autosetupmerge = always
    autosetuprebase = always

Note: This will ignore a file named ~/.gitignore in your home directory. In most cases, that's not a good idea to have such a file there. Instead, I suggest you add one in the root of your git repository. That one will be specific to your repository and everyone using it will benefit from that .gitignore file.

The editor line names the text editor that will be used in your console when you don't specify a message on the command line (-m). I like vim but if you prefer to use nano, emacs, etc. change that line. Note that by default the system will use the default editor which often is setup as nano. However, if you would like to change your default editor system wise, not just for git, then run this command instead:

sudo update-alternatives --config editor

And choose your favorite editor in the list. If you do not see your editor in the list, hit Ctrl-C, install it, then try this command again.

Local Drive Repository

Assuming you have a place such as /repo where you place all your CVS, SVN, and GIT repositories...

cd /repo
mkdir my-git
cd my-git
git init --bare
git update-server-info   # Only if you want to use via HTTP

NOTE: By default git still creates a master branch. If you want to change that default, you can use the following command:

git config --global init.defaultBranch main

Or edit your .gitconfig file and insert:

[init]
  defaultBranch = main

If, like me, you create git repository as root, make sure that setup is defined for the root user.

Sharing Your Local Drive Reporitory

On my end, I like to have the repository accessible by different local users ("semi-public"). To do so and still keep the repository safe, I set it up with the following permissions:

sudo su -
cd /repo
chgrp git my-git
chmod 2770 my-git
cd my-git
chown -R git:git .
usermod -a -G git alexis   # change the user name to your user(s)

The "2" in the chmod is used to allow and force the group to be setup as git in the sub-directories. This supposes that your user is part of the git group which is the adduser command is about. Note that the git group should already exists, it gets created whenever you install the git package.

Now we have a repository ready for use. Let's create a project and then add that project to the repository:

cd ~
mkdir my-project
cd my-project
[...create files, sub-directories, more files...]
cd ~/my-project
git init
git add *                # assuming you want to add everything you just created
git commit -a
git remote add origin /repo/my-git
git push -u origin master

Personalize a Repository

After the push the data is in the repository itself. Note that if you want to use it as yourself, then the /repo/my-git repository permissions can be set as you:

chmod -R my-name:my-group /repo/my-git

You'll get an error 13 if the permissions disallow you from doing anything in your new repository (i.e. if you had to use sudo anywhere to create the repository.) Otherwise you need to add yourself to some group and make sure that the repository has read/write access using that group.

This step is not required if all git users are allowed.

Sharing Repository

If you were creating that repository so someone else could access it, you probably want to test a clone as in:

git clone /repo/my-git

Obviously, an SSH or HTTP access would probably make it more useful to allow remote connections if you are sharing with lots of people. For example, when I use ssh I have a repo URL which looks like this:

git remote add origin ssh://git-server/repo/my-git/shared-git

The "git-server" part is a domain name. For example, were we to use our m2osw.com domain name for a git repository, we could write:

git.m2osw.com

The path, "/mnt/git", is whatever path that makes sense to you on your machine. Some people place git repositories under /opt and others under /var. Just choose something and stick to it. The standard place in Debian/Ubuntu is /var.

Then the "shared-git" part is the name of your project.

Moving a Remote Repository

To move youre remote repository, just copy all the files. You may want to compress the data to save bandwidth. There is nothing special to do in the remote repository itself. It can be reside anywhere.

However, when you move your remote git to a new server, you have to change the URL of your local instances with the following command:

git remote set-url origin ssh://new-git-server/repo/my-git/shared-git

The name (origin) may differ on your system or you may be using multiple of them. This is the default and probably correct for 99% of you.

You can verify which remote URL you currently have setup with the -v option:

git remote -v

Allow SSH Access

In order for the SSH protocol to be used, the server should have a git user and your user's public key should be authorized there:

sudo su -
(enter password to become root)
adduser git
cd ~git
mkdir .ssh
chown git:git .ssh
chmod 700 .ssh
cd .ssh
vim authorized_keys
chown git:git authorized_keys
chmod 600 authorized_keys

Then past your users' keys in that file and save.

Make sure that you setup the directory and file as owned by the git used. Also, change the permissions to only allow the owner to access the directory and file. If you don't do that right, the connection won't work because SSH considered your key tainted. Fix the permissions, then try again.

If you have other users on the computer, make double sure to change the .ssh folder ownership and permissions before you create the authorized_keys file.

Debug SSH Connection Issues

Often, your command generates an error without you knowing what really happened.

git clone user@domain:/path
...
fatal: Could not read from remote repository.
...

The issue is often not so clear. Was is not able to connect? Is the repository missing? Did you use the wrong domain name? Is it the username that's incorrect?

One way to know whether at least the SSH connection happens (or does not happen) is to add the -v command line option to the command. But how do you do tell git to do that?

It's actually quite simple, you add a variable ahead of the command like so:

GIT_SSH_COMMAND="ssh -v" git clone user@domain:/path

You can, of course, add any other command line options you'd like. If you think you need even more options, feel free to add more. In most cases, the -v is going to be enough to debug 99.9% of your SSH connection issues.

GitHub

Specifically, if you want to share on GitHub, you have to first create a git repository on github, then run the remote add command as in:

git remote add origin git@github.com:YourName/project.git

The "YourName" could also be a company name, a master project name, etc.

To find out what the "project.git" part is, you can see it in the project when you click the "Clone or Download" button. That will give you the URL. If you're logged in, it should show you the correct URL with the read/write git protocol. If you're not logged in, then you will be give a read-only HTTP resource. The path and project name should be the same, though.

If you added a README.md and a LICENSE file (and any other file, really), then you'll first have to merge by doing a pull before you can check in:

(WARNING: see below about the "main" branch before you pull/push)

git pull origin master

Fix any conflicts if you ended up with any, and then:

git push origin master

Your code is now on GitHub

Main vs Master

The github people decided to avoid using the word "master" so any new repository will have a "main" branch instead. Before you can do the first push to github, you will have to rename your master branch:

git branch             # make sure you are on the master branch
git branch -m main     # rename the branch

Then all references to "master" have to use "main" instead.

If branch "main" already exists, the renaming will fail with "-m". You can use "-M" (capitalized) instead to force the renaming to happen.

If you created a main with the "-b" option instead (i.e. branched from master to main instead of renaming), then you can go to your git repository to change the HEAD reference:

git symbolic-ref HEAD refs/heads/main

This has to be done in your origin tree. There is not other way to do it otherwise (not an easy one at least, without going bananas renaming things, etc.) After this last command, the default branch you download when you run git clone ... is going to be "main" as one would expect after such a change.

If you want to then delete the master branch, then you can use the following two commands:

git push -d origin master   # delete remote
git branch -d master        # delete local

Note that you are not prevented from later re-creating a master branch and deleting the main branch. You can also do the opposite: go to all your existing repository and create a main branch and delete the master branch. However, if you have many branches and many people all currently working on those branches, it's not going to be that easy to switch.

Add Sub-Module

In our snapcpp project, we use many sub-modules in the contrib directory so that way we have clean separate folders (they all have dependencies, though...)

To add a new module, it requires two steps:

1. Create a new package on its own in your github repository (see commands above)

2. Add that new package to the snapcpp project with the following:

cd snapcpp/contrib
git submodule add git@github.com:m2osw/snaplogger.git

As we can see, we use the URL to add the sub-module. That's what git uses to find the module and retrieve it.

Later, you want to comit and push each module as you make changes and push them... so...

a. go to your contrib module

b. make changes

c. commit + push

d. go to snapcpp/contrib

e. do a git commit <module> + git push origin master

Step (e) synchronizes the submodule to your latest changes so when someone checks out from snapcpp with the recursive flag, they get that latest version of that contrib sub-module.

Branchs

I wanted to add a few commands in link with branch. Below we see what to do when we have a head-less branch, but the basics can be applied in normal circumstances.

First, say you make changes and then want to create a branch. Assuming you did not commit, pull, or push, you can just do:

git checkout -b <branch-name>

That command also works if you did not yet make changes.

To switch between existing branches, you can use the checkout command without the -b flag. For example one can use:

git checkout main

to go back to main from your branch

Now if you want to work on a branch which a collegue started, it's not going to exist in your environment. That means a simple checkout will fail. First you actually need to fetch that branch:

git fetch origin <branch-name>
git checkout <branch-name>

Note that if you'd like to fetch all the branches, then you can simple omit the name of the branch on the fetch command line. If your policy is to keep all branches, though, it's going to be a long list after a while.

In many cases, people will tell you to just do:

git fetch

which for me pretty much never worked. Apparently, if you have a single remote, it is expected to work. In my case, I'm not aware of mor than one remote, so I have no clue why it fails... Adding at least the remote name ("origin" in the example above) works marvelously.

Forks

On github you can fork another project, check it out and make changes. Only there are two functions you may want to have access to in order to make this fork really useful.

First, add the upstream URL which gives you a way to access the original code from upstream/master (or some other branch). This gives you the ability to merge changes from the master branch in your version of the project without using a copy. It will automatically merge the changes made on the main branch.

git remote add upstream https://github.com/<owner>/<repo>.git

The remote add command allows you to add any number of remote repository. Here we add the upstream repository, the one you forked from. Since you are likely to not have direct access to the project, we use the HTTPS version (i.e. read-only version.)

To see the results, make sure that you have all the URLs as expected, you can use the -v option like this:

git remote -v

This will list your own remote URLs (if you have such) with git: (i.e. read-write) and then the two upstream URLs.

Next, you can get ready for a merge with a fetch command:

git fetch upstream

Finally, any time you want to merge with the code of the original author, run the following command:

git merge upstream/master

You will get the usual output, with the list of files that changed, how many changes, etc. Assuming you do not have any conflicts, no commit is required.

If you get conflicts, you will have to fix them and commit the fixes.

You should then push those changes to your own remote folder:

git push origin master

Note that merging and pushing to your master is also what is required to create a valid Pull Request. If you forget to merge the upstream, you may offer a Pull Request with code out of date which the authors are likely to reject.

A git repository for /etc

It is possible to make backups of your /etc directory by using a special configuration file using a quite bare definition:

sudo su -
<enter your password>
cd /etc
git init
git add .
git commit .

Then when you make changes to your /etc/ directory, or at least once in a while, make sure to update so that way you have some history:

sudo su -
<enter your password>
cd /etc
git add .
git commit .

If you need to, you can use git diff to compare with the older version. First check your logs to get the UUID and then the diff to see what changed:

git log
<search the point in time you're interested in, copy the UUID>
git diff -r <UUID>

You can do all the other commands as usual, of course. git rm, git mv, git checkout -b <branch-name>, etc. But managing branches in your /etc may not be practical. At least, you'll have a free copy of your files and some history, though.

HEAD-DETACHED Problem

In some cases, you will end up with a project which is "head detached". This means it's not in a real branch.

Especially, I get the problem with I checkout a project that has sub-modules. It is often that all the sub-modules are going to be detached. This is because the cloning will get a version which is not the latest. The version you have would be an intermediate between two other versions...

If you made no other changes, you can easily switch to master and to a

git checkout master
git pull origin master

to be up to date again. If that doesn't seem to work right for you, you can always use the method below skipping the merge step.

If you made changes that you want to add to master, you want to save the data to a named branch and then merge those changes to master. It is often that you don't notice that the git status is HEAD-DETACHED and you start making changes... I've done it many times mysefl.

git checkout -b fixes
git checkout master
git pull origin master
git merge fixes
(fix any issue + git commit if necessary)
git push origin master

This should get you going now.

Once you are sure it worked, you can delete the intermediate fixes branch with:

git branch -d fixes

Now you should be set.