Git Notes

Russell Bateman
last update:

These are quick reference notes; most of my Git notes are part of tutorials on Java Hot Chocolate.

Git configuration

I do this stuff:

$ git config --global "[email protected]"
$ git config --global  "Russell Bateman"
$ git config color.ui true
$ git config --global "[email protected]" ; git config --global  "Russell Bateman" ; git config color.ui true

How to make new Git commands using aliases

Use git config aliases:

$ git config --global alias.add-commit '!git add -A && git commit'

Then, use it like this:

$ git add-commit -m "commit-message"

If you don't like the alias you just created, ...

$ git config --global --unset alias.add-commit'

How to prime the pump of a new respository

This is what I do for GitHub and Bitbucket. I started out by managing my own git server using gitolite for about a year, but it's been many years now since switching to GitHub for work and Bitbucket for home (free even for private repositories) and managing a team git server is way more trouble than it's worth although there might be compelling reasons for a company to continue to do it.

But, if anyone asked me, I'd strongly encourage paying for GitHub or Bitbucket because it's not that much and you get automatic fire insurance plus secure access outside the walls without VPN. So, I don't remember how different this was for gitolite, but it shouldn't be too different, just replace with your own server name/IP address.

I do this fairly often, but not quite often enough ever to remember all the commands. It starts out WAY from scratch (so it's all here). For the first one, the subdirectory might already exist and it might (or not) already have stuff in it. No matter which, both cases are fine, just skip creating it.

$ mkdir /path/to/your/project
$ cd /path/to/your/project
$ git init
$ git remote add origin [email protected]:russellbateman/project.git

I don't ever do this, I usually add a

$ echo "russellbateman" && contributors.txt
$ git add contributors.txt

This is also optional, but I do it 'cause I like color and blame...

$ git config --global "[email protected]"
$ git config --global  "Russell Bateman"
$ git config color.ui true

I do this, obviously...

$ git commit -m "Initial commit with contributors"
$ git push origin master

How to remember what Git branch I'm working in...

To remember what Git branch you're working in, you can add it to your prompt. Yeah, I know: yet a longer prompt--just what I wanted too. The Git branch name is in purple. The one below was dug up originally somewhere on the web, but I got it from a friend. (To narrow the width reproducing it in this illustration takes up, I'm splitting it without adding spaces, backslash to escape the next line, etc. So, where there is a space, such as at the beginning of the line, it's completely intentional.)

Also, I'm trying to split it on syntactic elements, i.e.:

username@hostname: current working directory {branch} $

This will make it easier for someone reading this to divvy up where elements begin and end, and modify it more easily.

export PS1="\[\e[0;32m\]\u\[\e[m\]\[\e[1;33m\]@
 \[\e[0;35m\]\$(git branch 2> /dev/null | grep -e '\* ' | sed 's/^..\(.*\)/{\1} /')$

It looks approximately like this:

    bozo@clown-country:~/dev/myapp {russ} $

The subdirectory path is nigh invisible against a white-ish (light) background. It's for those who reject the (Apple Macintosh) notion that a computer console's purpose is to mimic the use of paper in the brick-, wood- and meat space.

Here's a better one for a white-ish console background. I also reserve the dollar sign for generic prompts preferring a right-facing caret.

export PS1="\[\e[0;32m\]\u@
 \[\e[0;35m\]\$(git branch 2> /dev/null | grep -e '\* ' | sed 's/^..\(.*\)/{\1}/')>

It looks approximately like this:

    russ@tuonela:~/dev/myapp {russ}>

Incidentally, if you're not in a subdirectory with files tracked by Git, then the branchname will not even appear.

    russ@tuonela:~/dev/downloads >

If you don't switch users, care about your hostname, etc., you can shorten the prompt dramatically. It has occurred to me that for years, I've maintained an informative prompt, much of which only reminds me who I am and what computer I'm on. I could see the utility of that if I came in via ssh, but otherwise, really, what do I care? And I even have a minimum of two Linux machines running in my den at home. It's just not that hard for me to guess which keyboard I'm typing at.

export PS1="\[\e[m\]\[\e[1;34m\]\w\[\e[m\]
 \[\e[0;35m\]\$(git branch 2> /dev/null | grep -e '\* ' | sed 's/^..\(.*\)/{\1}/')>

To look like this:

    ~/dev/myapp {master}> git checkout russ
    Switched to branch 'russ'
    ~/dev/myapp {russ}>

.bashrc entry for me

Here's an excerpt from my personal .bashrc on Ubuntu:

	if [ "$color_prompt" = yes ]; then
	  if [ "$using_git" = yes ]; then
	    export PS1="\[\e[m\]\[\e[1;34m\]\w\[\e[m\] \[\e[0;35m\]\$(git branch 2> /dev/null | grep -e '\* ' | sed 's/^..\(.*\)/{\1}/')> \[\e[m\]"
	    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]> '
	  PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w> '
	unset color_prompt force_color_prompt

See some stuff on console window color here.

How to remember what Git branch I'm working in (2)

Jo Liss published a marvelous addition to .basrc for this problem and I replaced what I'd been using (above) with it. I commented out one line because it erased displaying "master" and I want to see that ('cause that's the branch I usually forget and do work in when I don't mean to).

One this this does, though, is erase the title of the GNOME terminal window. I believe this is because the prompt (PS1) become a bash function invocation and gnome-terminal can't endure that anomaly for its title bar.

What may screw up later on... If Git is changed to return different English strings, this script will have to be modified.

# -------------------------------------------------------------------------
function _git_prompt()
  local git_status="`git status -unormal 2>&1`"
  if ! [[ "$git_status" =~ Not\ a\ git\ repo ]]; then
      if [[ "$git_status" =~ nothing\ to\ commit ]]; then
        local git_state_color=42
      elif [[ "$git_status" =~ nothing\ added\ to\ commit\ but\ untracked\ files\ present ]]; then
        local git_state_color=43
        local git_state_color=45
      if [[ "$git_status" =~ On\ branch\ ([^[:space:]]+) ]]; then
#       test "$branch_name" != master || branch_name=' '
        # Detached HEAD.  (branch_name=HEAD is a faster alternative.)
        branch_name="(`git describe --all --contains --abbrev=4 HEAD 2> /dev/null ||
          echo HEAD`)"

      # Insert color and branchname into prompt here:
      echo -n '\[\e[0;37;'"$git_state_color"';1m\]'"$branch_name"'\[\e[0m\] '

function _prompt_command()
  PS1="`_git_prompt`"'\[\e[1;34m\]\w \$\[\e[0m\] '

# -------------------------------------------------------------------------

Git and binaries

Git has a heuristic for detecting binary files.

You can force file types to be binary by adding a .gitattributes file to the repository (just as you would add a .gitignore file.)

This file contains a list of glob patterns followed by attributes to be applied to files matching those patterns. Subsequent clones of the repository will pick this up as well.

For example, if you want all files extended by .foo to be treated as binary files you can have this line in .gitattributes:

*.foo -crlf -diff -merge

This will mean all .foo files will

  • not have carriage return/line feed translations done
  • not be diff'd
  • merges will result in conflicts leaving the original file untouched

Git aliases

At some point in Git's history, the ability to specify aliases arose.

$ git config --global alias.please 'push --force-with-lease'
$ git config --global alias.shorty 'status --short --branch'

...and, of course, these are used thus:

$ git please
$ git shorty

Git tagging

There are two types of tags in Git, annotated and lightweight, differing mostly in the amount of metadata they can associate. A tag is identified via its tag identifier. In some of the examples below, this is v1.0.0.

Tagging for version management is the best option as compared to feature branching which gets messy (juggling multiple long-lived branches).

Annotated tags

These store extra metadata like

  • author name
  • release notes
  • tag message
  • date full objects in the database. These are important for a public release of the project. Here's the command, slightly reminiscent of git commit:

$ git tag -a v1.0.0 -m "Releasing version 1.0.0"

The tag created is only a reference to your local repository. It's not automatically pushed to your remote. To do this:

$ git push origin v1.0.0

Lightweight tags

These are the simplest way to tag the Git repository. They only store the hash of the commit they refer to and contain no other information. They are little more than bookmarks.

$ git tag v1.0.0

Listing tags

This is done with the command git tag. You can also filter what is listed with this command:

$ git tag -l 'v1.*'

Tag details

To show details of an annotated tag (lightweight tags have no details), do

$ git show v1.0.0
tag v1.0.0
Tagger: rbateman
Date:   Wed Jun 16 13:50:33 2021 + 0700

Release version 1.0.0
--------BEGIN PGP SIGNATURE---------
Version: GnuPG v1

--------END PGP SIGNATURE-------

commit 7d44b6bb8abb96dee33f32610f56441496d77e8a
Author: rbateman
Date:   Wed Jun 16 10:25:53 2021 + 0700

    Refactored a test.

Errors occurring in Git tags

If you attempt to create a tag that already exists (by tag identifier, that is), Git will issue an error:

$ git tag v1.0.0
fatal: tag 'v1.0.0' already exists

Deleting a tag

There should never be a reason to remove a tag because they cost almost nothing.

$ git tag -d v1.0.0
$ git push origin :v1.0.0

Recreate a specific version

The point of tagging is so that you can switch to the code just being what existed at the creation of that tag:

$ git checkout v1.0.0 -b v1.0.0

Best practices

...for branching and tagging.

  1. Be sure to branch your repository before you...
    1. are about to implement major or disruptive changes,
    2. are about to make (experimental) changes that might not be kept,
    3. are told to branch out.
  2. Note that, if you branch out, you must keep in close synchronization with branch master because you'll ultimately need to merge back to that branch. If you have not kept up, merging may be a big nightmare.
  3. Think of tags are bookmarking a point in time in the repository, however, you should use tags to mark released versions. In this way, if you need to make bug fixes to earlier releases (than the present), you can easily access the code for that release as well as create a branch for your work.
  4. Therefore, tag the repository (do not branch) to indicate a version. Semantic versioning, which is a best practice, calls for
        MAJOR   . MINOR   . PATCH           (or)
        VERSION . RELEASE . BUILD           (starting with)
        0       . 1       . 0               (and incrementing from there to)
        1       . 0       . 0               (once software is in production)
  5. Delete only branches from your repository that have been successfully merged back into master/HEAD.