why unix | RBL service | netrs | please | ripcalc | linescroll
git administration

git administration


One of the most important things with a git infrastructure, as with any, is a correct and tested backup strategy. A backup without testing is next to useless. I cannot repeat this enough, test your backup!

 $ cd `mktemp -d` && git clone /usr/local/git . && git bundle create /usr/local/archive/git-`date --iso-8601=s`-$$ --all

work flow

Two models that I have seen used many times are mainline and tofu.

The mainline model assumes that all workers pile their changes into a single location and you just assume that stuff works.

Large teams need a working strategy, tofu seems to be it. The method is simple, consider three dumping grounds, one where development work goes, another where you collaboration and testing of your product happens, and a final third location where you tag your releases with final versions.

In git terms the dev area is very straight forward, you take your feature branches, perform tests and upon success merge (pull request) back to home. When everything is deemed good tags (versions) can be given to final releases.

This is a copy-up approach. The tofu model considers the dev (feature branches) to be very fluid and subject to rapid change. The main collaboration area is a little more stable, things should only end up there once they've been proven good in some way or form. The final releases will have passed more stringent testing and documentation should be proof.

Faults found in final releases are fixed by edit, which should then be merged back to the main area and subsequently merged to the dev area, in that order. This ordering is important as it is the opposite of how data flowed there in the first place.


In git-terms, managing release fixes and branches is not overly complex. If you provide an online webpage then you don't have much of an issue as your service is a single version. If you provide an API service then you may need to manage older versions, too.

$ export T=`date`; echo $T >> readme.txt && git add readme.txt && git commit -m "$T" readme.txt
$ git checkout -b release1
$ git checkout -b live
$ git checkout -b beta
$ git checkout -b dev

Merge down, copy up, is shown in this orientation:

 --+-----------------------------------*--------------- release1
    \                     /^           |
     +--+------------------------------v--------------- live
         \              /^             |
          +----+-----------------------v--------------- beta
                \     /^               |
                 +---*----*----****---*v----*---------- dev

If you work on dev, when you commit you changes, you 'copy' them full to beta, then to live, eventually. This ensures changes go in full.

$ git checkout beta
$ git merge -Xtheirs <commitid>

If something is broken in release1 (for example the colour of something is wrong), then that fix delta needs to be 'merged' into live, then beta, then dev).

$ git checkout release1
make changes, and commit
$ git checkout live
$ git merge commitid

The thing to note here is that when going from fluid (dev) to rigid (release) you need to accept 'theirs' (dev) when resolving conflicts. When going the other way you're taking fragments (bug fixes) so will not want to auto accept a merge as there likely are conflicts. Some of the time you get lucky and there are no conflicts.


To sync your master copy with the origin you would normally:

$ git push

If your upstream (origin) has changed since the last time there was a pull, you may receive the following style of message:

$ git push
To /git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to '/git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

If you've modified your master copy, then the correct way to see what's changed is:

$ git diff origin/master master

To update your work with this

$ git merge

You may now

$ git push

file merging

The best scenario for to explain the objective here is if you imagine yourself as a web developer. You've just written a set of scripts with some company branding. Each of the pages/scripts have the same title. During the course of your work the company is bought and sold so the title has to change to reflect the new owners.

Being a good programmer you create a new branch and start to modify the first file and commit the change. You see this in the 'reflog':

0e4c8bb HEAD@{0}: commit: home page file

What you can do here is extract the diff between the change and it's previous version:

git reflog -p -1

Fortunately we can pipe this into patch to apply to the other file:

git reflog -p -1 | patch -p0 sales.pl

Once you're happy, merge the new branch to the parent. Using this technique avoids repeating the same change several times and introducing errors.


If you find yourself running git status to see what you have been working on and wish to add/commit that, you may find this little macro useful so that you don't have to traverse into the working root first:

git rev-parse --show-toplevel

You can easily alias that to something shorter, such as toplevel, then run:

git add `toplevel`/dir1/file1

upstream edited merge

You created a PR which was accepted with some minor alterations. Great! Did you forget to create a branch first? If so your master and upstream's master with be different. Fear not.

  1. Add upstream as a remote (git remote add upstream ...)
  2. Take your additions (git format-patch -1 usually is all that is needed, review the output file)
  3. Pull from upstream (git pull upstream) and then checkout upstream's tip (git checkout upstream/master)
  4. Now create a branch to work on (git branch new_work)
  5. Bring in your changes (git am <output from format-patch>)
  6. Push and create your new PR

This is not a bad habit to get into. You should almost always be working from upstream's master branch.

how to do debugging with gdb

This sort of belongs here as it is a common pattern.

Perhaps when working on a bug the essential thing to do is setup gdb with text user interface:

tui enable

Also set a breakpoint:

break broken.c:print

and for convenience, run:


Now, store these commands in a command file so that when you run gdb it can execute the instructions, perhaps something like this:

cat > gdb_run << EOF
tui enable
break broken.c:print

make && gdb ./src/program --command=gdb_run

date of a commit

Sometimes to keep things in order you may need to change the date of a commit, that can be done easily with:

git commit --amend --no-edit --date "`date`"