Git

From Organic Design wiki
Revision as of 17:35, 15 June 2015 by Nad (talk | contribs) (Inserting into history)
Broom icon.svg The content of this article requires cleaning up to meet OD's quality standards. Check the wiki best practices for guidelines on improving article and categorisation quality.

Git is a distributed revision control / software code management project created by Linus Torvalds, initially for the Linux kernel development.

Git's design was inspired by BitKeeper and Monotone. Git was originally designed only as a low-level engine that others could use to write front ends such as Cogito or StGIT. However, the core Git project has since become a complete revision control system that is usable directly. Several high-profile software projects now use Git for revision control, most notably the Linux kernel, X.org Server, One Laptop per Child (OLPC) core development, and the Ruby on Rails web framework.

Git differs from systems such as CVS, or SVN in that the database is maintained beside the working filesystem on peers. Each peer is easily sync'ed to any other by using push/pull or fetch. In subversion you use three main directory structures;

  • trunk
  • branch
  • tag

In git the trunk is equivalent to HEAD, and is a sha1sum to the latest commit. Branches are used to fork development from the HEAD if for example bug fixing is required. A tag in git is just a named sha1sum commit which is effectilvely a static reference to a particular snapshot of code.

If a repository is cloned, git tracks the master which is the latest commit on the remote repository, as well as the origin/master which is the last known commit from the source sourced repository. This is updated if your remote changes are pushed back to the repository you cloned from.

Using Git to update Wikimedia extensions

git clone https://gerrit.wikimedia.org/r/p/mediawiki/extensions/<EXT>.git

This clones the entire repo including all revisions to your local system. To change the local directory structure to a specific revision, use checkout with the required commit hash, e.g.

git checkout 6854b712f200a833220c156a2499f502317c20c1

Reverting a working copy

git reset --hard <branch/tag/rev>

Updating just a single file

git fetch
git checkout origin/master -- path/to/file

Links to Setting up Git Servers

Github Webhooks

Github offers notifications via Webhooks so that services can respond dynamically to events occurring on their repositories. We use this to have some clones on the server automatically update whenever a push occurs. Here's an example PHP script that responds to Github notifications that are in JSON format (the default) with the optional secret used to validate the request. Note that the web-server must have permission to execute git pull on the repo in question.

if( array_key_exists( 'HTTP_X_HUB_SIGNATURE', $_SERVER ) ) {
        $sig = $_SERVER['HTTP_X_HUB_SIGNATURE'];
        $body = file_get_contents( 'php://input' );
        $hmac = hash_hmac( 'sha1', $body, 'SECRET' );
        if( $sig === "sha1=$hmac" ) {
                $repo = json_decode( $body )->repository->name;
                exec( "cd /PATH/TO/LOCAL/CLONES/$repo && git pull" );
        }
}

Changing history with Git

One interesting thing I had to do on a Git repository, which could me required again, was to insert commits into the history. I had migrated a site over to Github that had previously been worked on directly over FTP. After migrating the site to Github and working on it for a week or so, I discovered that many of the directories had a lot of backup versions of files with the date it was backed up in the filename. Obviously it would be much more organised if those backup files could actually be inserted into the revision history of their associated file. So here's a general procedure for how to insert revisions of a file into history.

Here's an example repository containing a file called foo.txt which has three revisions.

git log --oneline
e4e7230 C state
82f95e9 B state
d6f8638 added first file

I want to place two extra revisions before the "B state" revision, so first lets wind the head back to the commit before that,

git reset --mixed d6f8638

Now we add our two new commits (which I've commented as A.1 and A.2), then check what the log looks like. We can see our two new commits forked off from the first commit.

git log --oneline
71acf31 A.2
f8cfb9b A.1
d6f8638 added first file

Then we wind the head back to the top, which is now a detached fork, so we make it into a branch called tmp so we can refer to it more easily.

git checkout e4e7230
git checkout -b tmp

This tmp branch is connected at the first commit since that's where we went back to and forked by making new commits there, so we can use rebase to disconnect it from there and connect it to the new end of the master branch.

git rebase master

This creates a conflict though because the current master version of foo.txt and the version at the start of the tmp branch are different, so we need to edit that file and select the latest of the two presented versions (in this example the "B state" version). Then we add the file and continue the rebase operation. [Edit conflicted foo.txt and use the most recent]

git add foo.txt
git rebase --continue

Now let's see what we've got:

git log --oneline
ca94250 C state
5e97ab9 B state
ffd055c A.2
1a9ff08 A.1
d6f8638 added first file

That's just what we wanted, the original three commits with our extra two inserted in. The only problem left to fix up is that we're in the tmp branch, the master's head is still at A.2. So all we need to do to tidy up is merge the tmp branch into master and delete delete it.

git checkout master
git merge tmp
git branch -d tmp

See also