Gitting started with git II: Beyond gitting started

Now that you know how to work with your own git repository, let’s look at some more advanced used-cases.

1. Collaborating on a VisCog project

The VisCog group has its own GitHub page, with an unlimited number of public and private repositories. Every member of the VisCog group has read and write access to all VisCog repositories (unless you set some special settings). This makes it easy to collaborate within the group.

1.1 Cloning a VisCog repository

You can create a local copy (or “clone”) of an existing VisCog repository with the following command:

$ git clone https://github.com/viscog/<repo-name>.git

This will create a directory “repo-name” within your current directory. You can now change into the directory and start working.

1.2 Making updates to a VisCog repository

After cloning the repo, you can start working on your local copy, and commit local changes like we did in the previous lesson:

$ cd repo-name $ … make some changes …

$ git add changed_file.m

$ git commit -m “fixed bug”

To make the changes available in the cloud, again use the ”push” command:

$ git push origin master

1.3 Syncing local and remote repository

When you’re collaborating on a project, it’s possible for the local and remote repo to get out of sync. This happens, for example, if your collaborator makes a change while you’re away. To fetch the latest changes from the cloud and apply them to your local repo, type:

$ git pull origin master

On an active project, it’s a good idea to do this first thing in the morning (or afternoon, depending on when you usually get up) before you start working on your local copy. So a typical work flow for an existing project looks like this:

$ git pull origin master $ … make some changes …

$ git add changed_file.m $ git commit -m “fixed bug”

$ git push origin master

2. Creating branches

One of the coolest features of Git (if you consider Git cool) is branching. Remember the ”$git log” command from Git 101? It showed us a history of all our commits, organized in a straight line in time. However in most software projects, the git history looks more like a tree, with different branches growing alongside each other. By default, you are automatically on the “master” branch. That’s why we typed ”$ git push origin master” above. However, in more complicated software projects, the “master” branch is usually reserved as the place for all stable/working code. The real work is done on a different branch, such as “develop”. Every once in a while, the two branches are synced.

If you want to see a more complicated network graph, check out pulse2percept.

2.1 Checking available branches

You can see a list of all available branches with the following command:

$ git branch

If you want to include branches of the remote repository, you can use:

$ git branch –all

2.2 Creating new branches

In your local repo, you can create a new branch called “develop” (or whatever you wish) with:

$ git checkout -b develop

This corresponds to the top-most blue node in the figure above. Git will take all current code and copy it to a new branch “develop”. Now you can work on this branch, add and commit files as usual:

$ … do some work …

$ add changed_file.m

$ git commit -m “add this awesome new feature”

If you want to save the commits to the cloud, use the ”push” command again. But this time, instead of pushing the branch “master”, we specify “develop”:

$ git push origin develop

2.3 Switching branches

If you want jump back on to the “master” branch, you can use the ”checkout” command:

$ git checkout master

If you’re not sure what branch you’re on (and to see what branches are currently available), you can always run:

$ git branch

2.4 Merging branches

Now that we have made some changes on the “develop” branch, we would like these changes to be reflected on the “master” branch. For this we can use the ”merge” command. First, we switch back onto the “master” branch: $ git checkout master

Then we merge all changes from the “develop” branch into the “master” branch:

$ git merge develop

What git will do is go through all the files on the “develop” branch, compare them to the same files on the “master” branch, and merge the two files line-by-line. If a file exists only on “develop”, but not on “master”, it will simply be added to the “master” branch. If you’ve deleted a file on the “develop” branch that previously existed on the “master” branch, git will remember. You could see how this could go horribly wrong. For example, what if a file that existed on both branches got edited on both branches? How would git know which version is the right one? It can’t. This is where merge conflicts come in, and they need to be resolved by hand.

3. Fixing merge conflicts

The easiest way to fix merge conflicts is with a program called meld.

3.1 Installing meld

On Unix, you can install it via:

$ sudo apt-get install meld

On Windows, there is an installer.

3.2 Setting up meld as the git merge tool

You can tell git to use meld for resolving merge conflicts via:

$ git config –global merge.tool “meld”

On Windows, you’ll also have to set the path to the executable:

$ git config –global mergetool.meld.path “C:\Program Files (x86)\Meld\Meld.exe”

3.3 Fixing a merge conflict

A conflict will happen when there have been changes to the same file in both branches that you are trying to merge. In this case, the ”merge” command will fail with the following error message:

$ git merge develop Auto-merging file.txt CONFLICT (content): Merge conflict in file.txt Automatic merge failed; fix conflicts and then commit the result.

We can try to resolve the conflict by hand using meld:

$ git mergetool

This will open up meld, which presents you with three editor windows side-by-side. On the left-hand side you’ll see the file as it is on the current branch (i.e., the “master” branch). On the right-hand side you’ll see the file you’re trying to merge in (i.e., from the “develop” branch). In the center you’ll have the merged file, the one you’re actually going to save:

You can click on the arrows to merge individual lines of code into the center window:

Alternatively, you can also edit the center column by hand. We don’t care what the other files look like, we only care about the center column. When you’re done, click into the center column and press Ctrl+S to save the window.

Then close the window.

If there are more files that need merging, repeat the process.

Once all conflicts have been resolved, you can see all changes with the ”status” command:

$ git status On branch master All conflicts fixed but you are still merging. (use “git commit” to conclude merge)

Changes to be committed: modified: file.txt Untracked files: (use “git add <file>…” to include in what will be committed) file.txt.orig

Then simply commit the changes:

$ git commit -m “manually fix merge conflicts”

And done!

3.4 Aborting a conflict resolution (aka panicking)

You can always abort an attempt at resolving merge conflicts via:

$ git merge –abort 

4. FAQ

How do I go back in history to a previous commit?

You can go back in history using the ”checkout” command. Every commit has a hash string associated with it, which you can find via the ”log” command:

$ git log commit 4966b8fbe6a94b9269efaefce105e43b70e7a6f7 Author: Michael Beyeler <mbeyeler@uw.edu> Date: Wed Apr 5 14:29:11 2017 -0700 add latest model

Then cut-and-paste the commit hash into the next command:

$ git checkout 4966b8fbe6a94b9269efaefce105e43b70e7a6f7

Note: checking out ‘4966b8fbe6a94b9269efaefce105e43b70e7a6f7’. You are in ‘detached HEAD’ state.

You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example:

$git checkout -b <new-branch-name>
I made a typo in my last commit message. How do I edit that message?

The following command will put you back in the state right before you did ”$git commit”:

$ git commit –amend

This will open up your text editor and allow you to re-enter your commit message.

How do I delete a branch?

That depends. To delete a local branch, use the following command:

$ git branch -D <branch-to-be-deleted>

However, that will not affect your remote repo. To delete a branch from the remote, type (note the ”:”):

$ git push origin :<branch-to-be-deleted> 

How do I make git remember my GitHub username/password when I push?

If you’re tired of typing in your username/password every time you try to git push, try this:

$ git config credential.helper ‘cache –timeout=3600’

This will remember your password for 3600 seconds (1 hour). Feel free to increase the number to whatever you feel comfortable. For example, with timeout=604800 you will never have to type in your password again as long as you git push at least once a week.

How do I undo git init?

All git init does is to create the hidden folder .git. You should be able to see it with ls:

$ ls -alh

Then just delete the .git folder, and all is forgiven and forgotten.

$ rm -rf .git
I accidentally added/committed a large file, now GitHub won’t let me push. What now??

This is especially annoying if we’re talking about a binary file (e.g., .mat, .png). Every time there’s a change to the file, git has to store the entire file (because it can’t do line-by-line comparisons), therefore bloating up your git history and the size of your .git folder. git rm largefile.png won’t help, because git will store the information that you deleted the file (including the deleted file, so it can later be restored). The only way out is to remove any changes to the file from your git history:

$ git filter-branch –tree-filter’rm -f largefile.png’ HEAD

Use with caution!