This section helps you to get started with Git and gives you an understanding of the fundamental Git concepts.
Repository, Working Tree, Commit
First, we need to introduce some Git-specific terms which may have different meanings in other version control systems such as Subversion.
Classical centralized version control systems such as Subversion (SVN) have so-called `working copies', each of which corresponds to exactly one repository. SVN working copies can correspond to the entire repository or just to parts of it. In Git, on the other hand, you always deal with (local) repositories. Git's working tree is the directory where you can edit files and it is always part of a repository. So-called bare repositories, used on servers as central repositories, don't have a working tree.
Example |
Let's assume you have all your project-related files in a directory D:\my-project. Then this directory represents the repository, which consists of the working tree (containing all files to edit) and the attached repository meta data which is located in the D:\my-project\.git directory. |
Typical Project Life Cycle
As with all version control systems, there typically exists a central repository containing the project files. To create a local repository, you need to clone the remote central repository. Then the local repository is connected to the remote repository, which, from the local repository's point of view, is referred to as origin. The cloning step is analogous to the initial SVN checkout for getting a local working copy.
Having created the local repository containing all project files from origin, you can now make changes to the files in the working tree and commit these changes. They will be stored in your local repository only, so you don't even need access to a remote repository when committing. Later on, after you have committed a couple of changes, you can push them to the remote repository. Other users who have their own clones of the origin repository can pull from the remote repository to get your pushed changes.
Working Tree States
There are some particular situations where commits cannot be performed, for instance when a merge has failed due to a conflict. In this case, there are two ways to finish the merge: Either by resolving the conflict, staging the file changes and performing the commit on the working tree root, or by reverting the whole working tree.
Branches
Branches can be used to store independent series of commits in the repository, e.g., to fix bugs for a released software project while simultaneously developing new features for the next project version.
Git distinguishes between two kinds of branches: local branches and remote branches. In the local repository, you can create as many local branches as you like. Remote branches, on the other hand, are local branches of the origin repository. In other words: Cloning a remote repository clones all its local branches which are then stored in your local repository as remote branches. You can't work directly on remote branches, but have to create local branches, which are 'linked' to the remote branches. The local branch is called tracking branch, and the corresponding remote branch tracked branch. Local branches can be tracking branches, but they don't have to.
The default local main branch created by Git is named master, which is analogous to SVN's trunk. When cloning a remote repository, the master tracks the remote branch origin/master.
Working with Branches
When you push changes from your local branch to the origin repository, these changes will be propagated to the tracked (remote) branch as well. Similarly, when you pull changes from the origin repository, these changes will also be stored in the tracked (remote) branch of the local repository. To get the tracked branch changes into your local branch, the remote changes have to be merged from the tracked branch. This can be done either directly when invoking the Pull command in SmartGit, or later by explicitly invoking the Merge command. An alternative to the Merge command is the Rebase command.
Tip |
The method to be used by Pull (either Merge or Rebase) can be configured in Repository|Settings on the Pull tab. |
Branches are just pointers to commits
Every branch is simply a named pointer to a commit. A special unique pointer for every repository is the HEAD which points to the commit the working tree state currently corresponds to. The HEAD cannot only point to a commit, but also to a local branch, which itself points to a commit. Committing changes will create a new commit on top of the commit or local branch the HEAD is pointing to. If the HEAD points to a local branch, the branch pointer will be moved forward to the new commit; thus the HEAD will also indirectly point to the new commit. If the HEAD points to a commit, the HEAD itself is moved forward to the new commit.
Commits
A commit is the Git equivalent of an SVN revision, i.e., a set of changes that is stored in the repository along with a commit message. The Commit command is used to store working tree changes in the local repository, thereby creating a new commit.
Commit Graph
Since every repository starts with an initial commit, and every subsequent commit is directly based on one or more parent commits, a repository forms a 'commit graph ' (or technically speaking, a directed, acyclic graph of commit nodes), with every commit being a direct or indirect descendant of the initial commit. Hence, a commit is not just a set of changes, but, due to its fixed location in the commit graph, also represents a unique repository state.
Normal commits have exactly one parent commit, the initial commit has no parent commits, and the so-called merge commits have two or more parent commits.
o ... a merge commit
| \
| o ... a normal commit
| |
o | ... another normal commit
| /
o ... yet another normal commit which has been branched
|
o ... the initial commit
Each commit is identified by its unique SHA-ID, and Git allows checking out every commit using its SHA. However, with SmartGit you can visually select the commits to check out, instead of entering these unwieldy SHAs by hand. Checking out will set the HEAD and working tree to the commit. After having modified the working tree, committing your changes will produce a new commit whose parent will be the commit that was checked out. Newly created commits are called heads because there are no other commits descending from them.
Putting It All Together
The following example shows how commits, branches, pushing, fetching and (basic) merging play together.
Let's assume we have commits A, B and C. master and origin/master both point to C, and HEAD points to master. In other words: The working tree has been switched to the branch master. This looks as follows:
o [> master][origin/master] C
|
o B
|
o A
Committing a set of changes results in commit D, which is a child of C. master will now point to D, hence it is one commit ahead of the tracked branch origin/master:
o [> master] D
|
o [origin/master] C
|
o B
|
o A
As a result of a Push, Git sends the commit D to the origin repository, moving its master to the new commit D. Because a remote branch always refers to a branch in the remote repository, origin/master of our repository will also be set to the commit D:
o [> master][origin/master] D
|
o C
|
o B
|
o A
Now let's assume someone else has further modified the remote repository and committed E, which is a child of D. This means the master in the origin repository now points to E. When fetching from the origin repository, we will receive commit E and our repository's origin/master will be moved to E:
o [origin/master] E
|
o [> master] D
|
o C
|
o B
|
o A
Finally, we will now merge our local master with its tracking branch origin/master. Because there are no new local commits, this will simply move master fast-forward to the commit E (see Fast-forward Merge).
o [> master][origin/master] E
|
o D
|
o C
|
o B
|
o A