Team Work. Using Git

Overview

In order to organize team work a Git version control system can be used. Using Git has a number of benefits as compared with other ways to collaborate:

  • saving the history of changes with the possibility to roll back to previous versions
  • synchronizing changes between users and automatic merging of changes
  • working with large binary files is possible

Git is a distributed system and every developer or designer has his own local repository (storage). Syncing between the local repositories can be performed via the central (“shared”) storage, which can be located on a dedicated machine (server). Access to the server can be organized through SSH protocol.

Although there are many GUIs for Git beginners, here the work with the git standard console utility is explained.

Typical Workflow

  1. Files can be created, added or deleted during the work process in the local repositories.
  2. After a certain logical period of work is finished it is necessary to fix (commit) the changes and/or synchronize with your team mates.
  3. Files are prepared for commit i.e. the accounting of changed, new and deleted files and also the resetting of changes.
  4. Commit is performed.
  5. Local changes are uploaded into the shared storage and become available for the colleagues.

A limited set of Git commands recommended for authoring applications and their graphical resources is described below.

It’s necessary to switch to the repository before executing the commands, e.g.:

> cd ~/blend4web

Individual Settings

A new user can set up his name and email using the commands:

> git config --global user.name "Ivan Petrov"
> git config --global user.email ipetrov@blend4web.com

The set up data will be used in the changelog.

Checking the Status

It’s recommended to check the state of the repository before, in progress and after performing all the operations.

Use this command to check the status:

> git status

The result of the git status command if all the commits were performed and there are no new files:

# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
nothing to commit (working directory clean)

Possible result of git status if there are changes. For example the apps_dev/firstperson/firstperson.js and doc_src/git_short_manual.rst files are changed and a new file 123.txt is created:

# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   apps_dev/firstperson/firstperson.js
#   modified:   doc_src/git_short_manual.rst
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   123.txt
no changes added to commit (use "git add" and/or "git commit -a")

Before the Commit

Checking changes (of the text files)

In case of text files it is recommended to view the introduced changes before performing the commit.

Check what was changed in the whole directory:

> git diff

or in a specific file only:

> git diff apps_dev/firstperson/firstperson.js

A possible result of the git diff command for a text file:

diff --git a/apps_dev/firstperson/firstperson.js b/apps_dev/firstperson/firstperson.js
index 4381c99..44b3b15 100644
--- a/apps_dev/firstperson/firstperson.js
+++ b/apps_dev/firstperson/firstperson.js
@@ -557,8 +557,9 @@ function enable_camera_control_mode() {
             var cam_view_down = CAMERA_MOVE_UPDOWN * (Math.sin(_passed_time) - 1);

             b4w.camera.translate_view(obj, 0, cam_view_down, cam_view_angle);
-        } else
+        } else {
             b4w.camera.translate_view(obj, 0, 0, 0);
+        }
     }

Rolling back files

If the file was changed or deleted but it is necessary to recover it (to the latest committed state) use the command:

> git checkout doc_src/git_short_manual.rst
> git checkout 123.txt

The introduced changes will be canceled - this is why this command should be performed with caution.

Unwanted files

If a file is listed in the Untracked files (git status), but version control is not needed for it, it should be deleted or moved beyond the working directory.

Preparing for Commit

Adding files

If you are happy with the changes, add the needed changed and/or new files for commit.

> git add apps_dev/firstperson/firstperson.js
> git add 123.txt

Check the status again:

> git status

Possible result of the git status command after adding some files with the git add command:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   123.txt
#   modified:   apps_dev/firstperson/firstperson.js
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   doc_src/git_short_manual.rst
#

You can see that the apps_dev/firstperson/firstperson.js and 123.txt files were added for commit and the doc_src/git_short_manual.rst file was not added. To make things easier it is recommended to either add such files for commit or cancel their changes with the git checkout command.

Removing files

Some files can be marked as deleted from Git after performing the git status command, for example:

# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    123.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

In this case if deleting the file should be recorded (i.e. enter the commit), perform the git rm command, for example:

> git rm 123.txt

If the file was deleted by accident and its necessary to recover it, use the git checkout command.

Commit

Perform commit with the command:

> git commit

A text editor window will show up (for example, nano or vim), in which it’s necessary to enter the commit comment in English.

  GNU nano 2.2.6                                    File: .git/COMMIT_EDITMSG

My commit message
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   123.txt
#       modified:   apps_dev/firstperson/firstperson.js
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   doc_src/git_short_manual.rst
#

^G Get Help               ^O WriteOut               ^R Read File              ^Y Prev Page
^X Exit                   ^J Justify                ^W Where Is               ^V Next Page

Save the changes and quit the editor (in nano Ctrl+O, then Ctrl+X; in vim ZZ, or ESC :wq).

After commit it’s recommended to recheck the status. Commit is performed correctly if the git status command returns nothing to commit, working directory clean.

Syncing Between Repositories

From the remote - to the local

After all the commits are performed it’s necessary to load the changes from the remote (“shared”) repository to the local one:

> git pull

Result of the git pull command if there are no changes in the remote repository:

Already up-to-date.

Result of the git pull command if the remote repository contains changes and syncing was successful:

remote: Counting objects: 151, done.
remote: Compressing objects: 100% (101/101), done.
remote: Total 102 (delta 74), reused 0 (delta 0)
Receiving objects: 100% (102/102), 69.77 MiB | 4.87 MiB/s, done.
Resolving deltas: 100% (74/74), completed with 32 local objects.
From lixer:blend4web
   dbf3877..9f9700c  master     -> origin/master
Updating dbf3877..9f9700c
Fast-forward
 apps_dev/firstperson/firstperson.js                |  338 +--
 .../location_agriculture.blend                     |  Bin 25601626 -> 25598644 bytes
 ...
 src/controls.js                                    |   38 +-
 src/data.js                                        |    5 +
 src/physics.js                                     |  185 +-
 19 files changed, 1452 insertions(+), 2767 deletions(-)
 create mode 100644    deploy/assets/location_agriculture/textures/rotonda_02_diff.png

If you wish it’s possible to look up the changes made by your colleagues using the following command:

> git diff dbf3877..9f9700c

The parameter of this command - in this case dbf3877..9f9700c - shows between which commits exactly the changes were made. This parameter can be conveniently selected in the console in the git pull results and pasted with a mouse click (middle button) where you need.

You can also view the changelog:

> git log

The git pull command does not always lead to a successful synchronization. The result of git pull when there are conflicts:

remote: Counting objects: 11, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 6 (delta 5), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From lixer:blend4web
   ff715c2..dbf316a  master     -> origin/master
warning: Cannot merge binary files: blender/landscape_objects/Fallen_tree.blend (...)

Auto-merging blender/landscape_objects/Fallen_tree.blend
CONFLICT (content): Merge conflict in blender/landscape_objects/Fallen_tree.blend
Automatic merge failed; fix conflicts and then commit the result.

The steps to be taken at conflicts are described below.

From the local - to the remote

After that the changes should be uploaded from the local repository to the remote (“shared”) one to make the changes available for team mates.

> git push

The result of the git push command if the remote repository already contains all the local changes:

Everything up-to-date

The result of the git push command if synchronization was successful:

Counting objects: 25, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 1.23 KiB, done.
Total 14 (delta 11), reused 0 (delta 0)
To gfxteam@lixer:blend4web.git
   9f9700c..fa1d6ac  master -> master

The result of the git push command if synchronization was not successful because the git pull command was not executed first:

To gfxteam@lixer:blend4web.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'gfxteam@lixer:blend4web.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.

You should execute the git pull command.

The changes uploaded into the central repository can be received by other developers with the git pull command.

Resolving Conflicts

Overview

Synchronization conflicts occur if both conditions are met

  1. the same file was changed both in the local and remote repositories, and
  2. automatic merging of the changes didn’t occur because the changes are in the same place of the file.

Typical cases:

  1. a binary file (texture, blend file) was independently changed by two developers
  2. different changes were introduced to the same line of the same text file
  3. one developer has changed the file while the other has moved it and so on.

Although synchronization conflicts are normal, if they happen too often it slows down the work. It is recommended to notify your team mates about start of working with the shared binary files, and also to perform synchronization more often. It is necessary to effectively distribute the work between developers to reduce the number of such shared files. This can be achieved particularly through linking of all the scene’s resources from the separate blend files into the master file.

The steps to be taken

It’s not recommended to perform any files operations (modifying, deleting) while the repository is in a conflict state.

The first thing to do is to perform the git status command.

# On branch master
# Your branch and 'origin/master' have diverged,
# and have 7 and 1 different commit each, respectively.
#
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   both modified:      blender/landscape_objects/Fallen_tree.blend
#
no changes added to commit (use "git add" and/or "git commit -a")

A list of conflicting files can be found in the Unmerged paths section.

The order of the following steps is different for binary and text files.

Binary files

At this stage the conflicting binary files are in the same state as they were in the local repository before the synchronization attempt. The files are fully functional (for example they can be opened by graphics editors).

In case of conflicting binary files it’s necessary to sort out (with the team mates or by yourself) which of the files should be left and which should be discarded. Selecting can be performed with the git checkout command.

Select the local version of the file (- -ours). To make sure that it’s local you can open it.

> git checkout --ours blender/landscape_objects/Fallen_tree.blend

Select the remote version of the file (- -theirs). To make sure that it’s remote you can open it.

> git checkout --theirs blender/landscape_objects/Fallen_tree.blend

Select the local version of the file again (- -ours).

> git checkout --ours blender/landscape_objects/Fallen_tree.blend

Eventually you have to stick to the right version of the file. In case there is a threat of losing the work you may save the discarded file outside the repository.

Text files

At this stage Git introduces both local and remote changes to the conflicting text files, in a special format. Such text files are not workable as a rule

Example. One developer changed the scene name from “Blue Lizard” to “Green Lizard” in the application file and uploaded the changes into the central repository. Another developer changed “Blue Lizard” to “Red Lizard” in the same line, performed commit and executed the git pull command. As a result this very developer will be responsible for resolving the conflict. The following lines will be present in his version of the application file:

<<<<<<< HEAD
                "name": "Red Lizard",
=======
                "name": "Green Lizard",
>>>>>>> 81bf4e2d5610d500ad4d2a2605ee7e61f759f201

In case of conflicting text files the following steps can be taken. Files with source code should be edited with or without respect to the changes introduced by both parties. On the other hand, it is easier to reexport the exported scene text files (ending with .json).

Correcting commit

After selecting the required files or editing the changes, add them for commit:

> git add blender/landscape_objects/Fallen_tree.blend
> git status

Possible result of git status command after adding the conflicting files for commit:

# On branch master
# Your branch and 'origin/master' have diverged,
# and have 7 and 1 different commit each, respectively.
#
nothing to commit (working directory clean)

Perform commit. It is recommended to leave the default comment:

> git commit
> git status
# On branch master
# Your branch is ahead of 'origin/master' by 8 commits.
#
nothing to commit (working directory clean)

Conflicts are resolved, the changes from the remote repository are successfully applied in the local repository. Now the changes in the local repository - including the just resolved conflict - can be uploaded to the remote repository with the git push command.

Tags

Tags are intended for pointing at a certain commit, for example, to specify a stable product version.

View the list of tags:

> git tag

Create a tag for the release from June 3, 2013, pointing to the commit with a stable product version:

> git tag R130603 67bb597f7ed1643ed0220d57e894f28662e614e5

Check the commit tag information:

> git show --shortstat R130603

Roll back to the tag...

> git checkout R130603

...and return:

> git checkout master

Synchronize the tags with the remote repository:

> git push --tags

Delete the tag (if created by mistake):

> git tag -d R130603

Other Useful Commands

Check the log for January, 2012, show file names without merging commits:

> git log --after={2012-01-01} --before={2012-01-31} --name-only --no-merges