Category Archives: Git

Changing the origin of your local git repo.

Git, being a distributed source control system, allows us to switch the remote/origin for the push or fetch of our local repository. Or to put it another way…

We’ve just moved from one remote git repository to another, how do we update our local code base to “retarget” to the new location?

When you’re using GitHub, GitLab, bitbucket etc. you might come to a point where you migrate from one server to another. Ofcourse you can simply clone your repo. again from the new server OR you can just target you fetch/push origin to the new location like this…

To check your current remote/origin, just run

git remote -v

Now if you wish to change the remote/origin, simply use

git remote set-url remote_name remote_url

Where remote_name might be origin and the remote_url is the new .git URL of your server.

Migrating a folder from one git repo to another

I had a situation where I had a git repo. consisting of a Java project and a C# project (a small monorepo), we decided that permissions for each project needed to differ (i.e. the admin of those projects) and maybe more importantly in a way, changes to one were causing “Pending” changes to the other within CI/CD, in this case TeamCity.

So we need to split the project. Ofcourse it’s easy to create a new project and copy the code, but we wanted to keep the commit history etc.

What I’m going to list below are the steps that worked for me, but I owe a lot to this post Move files from one repository to another, preserving git history.

Use case

To reiterate, we have a Java library and C# library sitting in the same git code base and we want to move the C# library into it’s own repository whilst keeping the commit history.

Steps

  • Clone the repository (i.e. the code we’re wanting to move)
  • CD into it
  • From a command line, run
    git remote rm origin
    

    This will remove the remote url and means we’re not going to accidently commit to the original/source repository.

  • Now we want to filter our anything that’s not part of the code we want to keep. It’s hoped that the C# code, like ours, was in it’s own folder (otherwise things will be much more complicated). So run
    git filter-branch --subdirectory-filter <directory> -- --all
    

    Replace with the relative folder, i.e. subfolder1/subfolder2/FOLDER_TO_KEEP

  • Run the following commands

    git reset --hard
    git gc --aggressive 
    git prune
    git clean -fd
    
  • Now, if you haven’t already create a remote repository, do so and then run
     
    git remote add origin <YOUR REMOTE REPO>
    
  • // this should have been handled by step 6 git remote set-url origin https://youreposerver/yourepo.git

  • git push -u origin --all
    git push origin --tags
    

Using .mailmap

When we create a commit in Git we get the author field listing the author’s name and email address. There are occasions where maybe an author is using different email addresses or names and we’d like to “fix” the commits to show the current or correct information for a commit.

Just add a file named .mailmap into the root folder of your project which contains the name of an author along with any email addresses that are to link to that author, for example

Bruce Dickinson  <bruce@im.com> <bruce@samson.net>

See mailmap for more information.

Git tags using the CLI

Creating tags from the command line…

Tags allows us to store a pointer to the repository at a point in time, these are often used for “tagging” a release, but can be used for other purposes.

Note: I will use the tag name v1.0.2 here, obviously this should be replaced with the tagname you’ve used/assigned.

Listing your tags

To see what tags you currently have on a repository run

git tag

this will list all your tags.

Creating your tags

There’s a couple of ways to create your tags, the first is the annotated tag

git tag -a v1.0.2 -m "Version 1.0.2"

Here we use -a (annotate) to create a new tag and -m (add a message). When creating an annotated tag, a message is expected and so you will be prompted to enter a message if you omit -m. Annotated tags store the message (although you could specify an empty message) along with the author of the tag.

Lightweight tags are another way to tag, these tags store no data with the tag (so do not supply -a or -m), for example

git tag v1.0.2

These tags store the commit checksum and whereas you’d tend to use the annotated tag for releases (for example) the lightweight tag might be used simply to label certain commits.

We can also tag by using the commit checksum (the 6a0b83 in the example below), this example uses a lightweight tag

git tag v1.0.2 6a0b83

Pushing your tags

Ofcourse, if we’re working with others we’ll want to share our tags, so if we have a remote to push to then we can push the tag using

git push origin v1.0.2

A simple git push does not push our tags, we need to be explicit or we can use the –tags switch to push all tags.

git push origin --tags

Viewing your tag

We’ve seen that git tag will list the tags but what about showing us the annotation tag data or what the tag actually refers to, this is where we use

git show v1.0.2

If it’s an annotated tag you see the author of the tag and any message along with the last commit within that tag, for the lightweight we simply see the last commit details.

If you’ve signed your tag (see Signing tags below) you’ll also see the GPG signature attached to the tag.

Deleting your tag

To delete a tag just use the following

git tag -d v1.0.2

If you’ve pushed your tag to a remote then use the following after the local delete

git push origin :refs/tags/v1.0.2

Or use

git push origin --delete v1.0.2

Checking out a tag

We can checkout the tag just like any branch using

git checkout v1.0.2

This will put your repository into a “detached HEAD” state. You cannot commit to this tag, or to put it another way, if you commit to this tag your commits will not appear within the tag, instead they’ll only be reachab;e by the commit hash.

If you intention it to make changes to a tag then instead, you need to branch from the tag, i.e.

git checkout -b v1.0.2-branch v1.0.2

Signing tags

Tags can be signed using GPG. The purpose of this functionality is to simply ensure that the tag was created by the person we expected. I’ve not so far needed to sign a tag (or commits for that matter, which can also be signed). However there may be a time when this is useful, so let’s go through it.

To check if you have a key installed type

gpg --list-keys

If no keys exist it’ll create the folders needed for creating keys.

To generate a key key run

gpg --gen-key

This will ask for your “real name” and “email address”, followed by a pass phrase and then this will create your key. Now running gpg –list-keys should list the newly created key.

Next we need to tell git to use our key using the following

git config --global user.signingkey 123456789

where 123456789 is replaced by the pub string associated with your key (see output from gpg –list-keys).

Now we can sign our tags using

git tag -s v1.0.2 -m "Version 1.0.2"

We replace -a with -s, this is now a signed, annotated tag.

We’ll probably want to verify our tag, and we do this using

git tag -v v1.0.2

If you have the public key installed you’ll see information about the key, if not then you’ll get a verification error.

Svn to git migration

I’ve been moving some repositories from SVN to GIT, so for reference, here’s the basic steps…

Note: These steps to not handle changing the author names etc. For a good explanation of this, checkout Migrate to Git from SVN.

Step are, as follows (these steps also assume you’ve create a repository within GIT for you code to migrate to)

  • git svn clone your_svn_repo
  • cd into your newly created folder
  • git remote add origin your_git_repo
  • git push -u origin master

Renaming a git branch

Occasionally you need to rename a branch you’re working on in git. The following shows how to rename the branch and push commits (assuming the branch had already been pushed) to a remote location

Note: if the branch has not been pushed to a remote location you only need to use the first two commands

  • git checkout <old_branch_name>
  • git branch -m <new_branch_name>
  • git push origin –delete <old_branch_name>
  • git push origin -u <new_branch_name>

gitconfig, the where and how’s

The git configuration files are stored at three different levels.

Local are stored within the cloned repository’s .git folder and the file is named config.

Global is stored in a file with no name and with the extension .gitconfig. It’s stored in your home directory. On Windows this can be confusing especially if the home directory is in a roaming profile. For example, normally we’d find the it in c:\users\your-user-name, however if you have a roaming profile then you’ll need to check

HOME="$HOMEDRIVE$HOMEPATH"

So for example this might end up as H:\

System is stored as gitconfig (filename but no extension). In the case of a Windows OS, this will be in C:\Program Files\Git\mingw64\etc, further configuration data may be found in the config file (filename but no extension) within C:\ProgramData\Git.

Scope

The scope of these files is as follows, local overrides global options and global overrides system.

List the configurations

If you execute the command below, you’ll see a list of all the configuration along with the location of the files used.

git config --list --show-origin

Note: beware if passwords are stored in the configuration then these will be displayed when you run this command.

The command does not show you what’s “actually” being used for configuration from the current repo. so much as “here’s all the configuration values”, so you’ll need to look at the scope of each file to determine what values are currently being used by git.

We can also use the same command along with the switch –local, –system and –global to list the files used along with the configuration used.

Further reading

git-config
git config (Atlassian)

Git aliases

In a previous post on git (Using GIT from the CLI), I listed a fair few commands which are very useful. For the most part they’re fairly simple commands/options, but occasionally they become less easy to remember.

For example to check for merge conflicts on a branch that you may wish to merge master to, you can type

git merge master --no-ff --no-commit

Luckily I have this blog post allowing me to copy and paste this command, an alternate might be to create an alias to this command.

Adding an alias

Creating the alias is simply a way to store a command name within git’s global config which runs another command. Whether we want to simply shorten a name, like using co and ci in place of checkout and commit or shorten a command such as the merge above with check-conflicts.

To create an alias we use the following

git config --global alias.check-conflicts 'merge master --no-ff --no-commit'

In this example we’re telling git to add configuration using –global option (so the configuration is across all repositories or –local for just this repository). We create the alias using the alias option followed by a period/full stop and then the name we want for our alias. This is then followed by the git command and options (within single or double quotes if there are multiple options).

Now we can execute

git check-conflicts

Deleting an Alias

To delete an alias we can either remove it from the config file (for example .gitconfig) or run the following command line

git config --global --unset alias.check-conflicts

Listing Aliases

We can look in the .gitconfig or run the following command line to list the available aliases

git config --get-regexp '^alias\.'

Creating an Alias to list Aliases

To round this up nicely, let’s now create an alias to list aliases, for example

git config --global alias.alias "config --get-regexp ^alias\."

Creating a pre-commit hook for TortoiseGit

Note: This is specific to using TortoiseGit to add your hooks.

I’ve covered Creating a pre-commit hook for TortoiseSvn previously for SVN. We can create similar hooks for GIT also.

Either create a script or other form of executable (this one’s a C# console application). This example is going to do nothing of real interest. It’ll simply stop commits to a repo. but is useful if we place a Debugger.Break before the Console line, for debugging purposes

static int Main(string[] args)
{
   Console.Error.WriteLine("No commits allowed");
   return 1;
}

A non-zero return value indicates a failure and hence this code will effectively stop any commits to the repository it’s applied to.

The arguments sent to the method will be as follows

* 1st arg is the file name of a file which contains a list of files that have changed
* 2nd arg is the file name of a file which has the commit message
* 3rd arg is the folder being committed

Now we compile our application and place it in the .git\hooks\ folder and we need to name it pre-commit.exe.

That’s all there is to it.

Advanced Git CLI

Having covered the “basic” commands in my previous post, I wanted to look at some of the more advanced stuff. I’m basically meaning things we probably won’t use as regularly as the previous post’s commands.

Rebasing

Rebasing isn’t necessarily advanced in it’s basic form but I’ve not had any real use for it so far apart from in more complicated scenarios.

Rebasing allows us to apply commits from, for example one branch, onto another branch. For example, let’s say we created a branch off of the wrong branch and so want to move commits from the incorrect branch onto the other branch.

So let’s assume we have feature/mybranch and we need to rebase it onto correct_branch, then we use

git rebase --onto correct_branch feature/mybranch 
git force push

Staging parts of a file

Git allows us to stage whole files/folders as well as parts of a file. For example, maybe we’ve made several changes, but we currently only want to stage a subset of these changes. We can use

git add --patch filename

// or short form

git add --p filename

We’ll now be presented with a list of the new lines and a lot of options. Selecting ? will list what each of the options means.

We can use commands such as e to edit the patch diff file, allowing us to delete lines etc. that are to be staged.

I’ll be adding further “advanced” situations/commands and when I need them.