GIT^ Version Control Software

GIT is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. It is complex and somewhat picky about who it appears friendly to. For a friendly web interface, and shared hub see GitHub:
https://github.com

Note: the following was written with msysgit on Windows, but it should apply to all versions and operating systems.

Starting a New Repo

To turn an existing folder into a git repository (aka "repo"):
git init
git creates a hidden metadata folder to store information about the changes made to files. This must be separate from the files, since only the currently checked out version of the file data will be in the file. The prior versions and branches will be in the hidden folder.

Note that files in the folder are NOT automatically committed to the repo, nor will they be included in a commit. You have to stage files to be tracked, and then commit them. Staged files are going to be version tracked. Keeping them separate from other files provides a way to focus tracking on only those files which need it.
Add files to the repo with
git add filename
you can remove files from tracking with
git reset filename

And then to commit those changes into the new repo (it doesn't automatically do that when you init) use commit. To automatically stage all the changes to the files you have added, include the -a flag. You can add an optional message.
git commit -a
will open the default command line editor (e.g. nano) for the commit message, or you can including it on the command line with:
git commit -am "message"
(by convention, the message should be a command. E.g. "Add stuff" instead of "Added stuff" or "Adding stuff"). If you choose not to include the message on the command line, git will open an editor window with a default message and expect you to edit that. By default, in Linux OSs, the editor used is vi. vi is only friendly to some users. Move cursor with arrows, press the i key to insert text, press Esc to stop inserting. Type :wq to save and quit. If something goes wrong, type :q to quite without saving. That is the : or colon key followed by q.

If you keep notes about your work in a separate text file, you can use those as the commit message by using the -F file option

Because it is important to ensure that each individual commit is a logical grouping of changes, it is sometimes useful to remove one or more files from tracking via the reset command before a commit, then re-add those files and make a second commit.

Local Clone of a Remote Repo

You can make a local copy of a remote repository (or repo) with the command:
git clone URL
This copies in ALL the changes to the files, not just the current version. By default, your local directory will show the head of the repo, usually the master branch, and the files will be as they are currently in the repo. If you are working from GitHub, the URL will be https://github.com/ProfileName/repoName.git

Existing files from the remote repo are already staged, and any changes to them are ready for a git commit. Any new files in the local clone will need to be added to the stage before being committed.

To update the local from the remote, use:
git pull origin master
assuming the name of the remote is origin (which it is by default) and the name of the local branch is master (which it is by default). See below for more.

Verson Control

We can see a simple list of the past changes to a repo with the command:
git log
issued inside the repositories main folder. The list includes unique commit ID's along with the data, author and comment about each entry.

Neat trick: Add this to .getconfig
[alias]
lg = log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold cyan)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all

(the line starting lg= is a single line and should not be wrapped as it is shown here) git lg will produce a nice graph of git branches.

To view the differences between two commit ID's use the command:
git diff commitID commitID
This will list each file that has changed, and the differences between the files in diff -c format.
git diff by itself shows the difference between the working files and the staging area (files which have been added, but not yet committed)
git diff --staged shows the difference between the staging area and the head commit.
git show commitID shows the difference between a commitID and it's immediate "parent"... the commit just before it.

To go back to a prior version or branch, use
git checkout commitID
commitID can be "master" to return to the head.

We can quickly see the status of the repo, including which files are not committed, by running
git status
This will list the current branch, the current commit, and which files are not being tracked or have been changed.

Forks / Branches / Experiments

Branches allow you to make completely separate versions, as opposed to revisions, of your files which are tracked separately over time and which you can switch between. It is always good to keep a known working branch, Master is the default branch, and then experiment with a named branch off the Master "trunk". Branches can also be used to allow multiple people to work at the same time on the same code by creating a branch for each person. Commits are only added to the branch you currently have checked out. You can merge branches (usually back to the master) if the experiment works out.
git branch
lists the current branches with a star next to the currently checked out branch.
git branch branchName
makes a new branch but does not check it out.
git checkout branchName
checks out a branch. New commits will be added to this branch.

Note that git log shows commits along the branch that is currently checked out. So there may be other commitID's which are not listed if they are on another branch. Having a diagram that shows all the branches would be really nice...

Because branches are basically just names for commitID's, we can view the differences between two branches with the command:
git diff branchName branchName

If you check out a commitID which is not at the end, or HEAD, of a branch, and then commit new changes from that revision, you will be in "detached HEAD" state. You are following a new, unnamed, path which is sprouting off from one of the named branches. The commits you make will not show up in logs after checking out a branch head, and will not be merged back into the Master if those branches are merged. To make this twig a new branch, use:
git checkout -b branchName
which both makes a new branch and then checks it out.

To merge branches back together, differences must be resolved. That can be difficult, but git can figure out most issues by itself by looking through the prior versions on each branch to see what was added or removed.

To start a merge, checkout the branch were you want to continue development from in the future. Normally, this is the Master branch. Then issue the command:
git merge branchName [branchName ...]
Note that you can specify as many other branches as you like, potentially merging them all back into the current branch.

If there is a conflict which can not be resolved automatically, git will report which files are in conflict and ask you to fix it. Note that the merge operation has still been started. To abort the merge, use:
git merge --abort
then you can use diff to view the files in conflict, resolve the conflict, add and commit those new file versions and try the merge again. Once possible source of conflict is line ending differences between operating systems. See: https://help.github.com/articles/dealing-with-line-endings/#platform-all

To continue the merge, and work with git to resolve the conflicts, don't abort the merge, but instead edit the files git called out in the error message. You will find that git has edited those files to include tags marking areas of conflict:

Edit the file to combine the code, retaining all the features you want. Work with others if you aren't sure what their changes accomplish. Be sure to remove the lines git added to mark the conflict. When you are finished, use
git status

to verify the new file shows "both modified" (meaning it was changed in both branches) and then use
git add filename

to mark the new version to be committed.

Once you have tested and compiled the files, finish by using
git commit

to complete the merge. The message git auto-generates will show what branches were merged and which files had conflicts.

After a merge, all the commits in the merged branches will show in the log in chronological order. As a result, the diff between commitID's may not show only the changes made by the parent commit. Use:
git show commitID
to display the changes from that ID's parent.

After a merge, if the branch name that was merged into the current branch is not wanted, it can be deleted with :
git branch -d branchName

Local Work with Remote Repository Servers, e.g. GitHub

GitHub.com is a web-based Git repository hosting service. It offers all of the distributed revision control and source code management (SCM) functionality of a standard remote Git repo server allowing multiple users to collaborate on a project through local Git repos. It provides access control and several collaboration features such as bug tracking, feature requests, task management, and wikis for every project.

You can make a local copy of a remote repository (or repo) on github with the command:
git clone https://github.com/ProfileName/repoName.git

To update the local from the remote, use:
git pull origin master
assuming the name of the remote is origin (which it is by default) and the name of the local branch is master (which it is by default). See below for more.

To make a remote copy of a local repo,

Troubleshooting. If it fails for user/pass without asking you for either, then there is a bad certificate helper installed. If it only asks for pass, the username may be pre-set. Those can be inherited from the global or default settings. Edit the local config file for the repo.

Collaboration

Conflicts must be resolved manually: There is no system for automatically sycronizing repos because too many conflicts require human review to resolve. Googles collaborative documents avoid this problem (for the most part) by re-syncronizing so quickly and constantly that conflicts don't have a chance to develop... but that causes conflicts between programmers making incompatible changes in the code all at the same time, and it moves a lot of data around, increasing overhead.

Fork at the Remote Repo: Even if you are going to clone a remote repo to work on it locally, when you are collaborating or want to extend or change code written by someone else, it's best to "Fork" that code on the remote so that there is a record of the source, and the possibility for the original author to pull your changes back into the main project. See: GitHub:Fork for detailed instructions.

Locals may be out of sync with Remotes. If changes are made by you in the local repo, and different, but non-conflicting changes are made by someone else to the remote repo, then there is no problem, just use the pull command to merge them back into your local repo:
git pull remoteName branchName

Resolve Conflicts: If there is a conflict, you will have to resolve it in several steps.

  1. It's always safe to "fetch" the remote repo by it's remote name (usually origin), which will updated a local branch called remoteName/branchName, or most often origin/master.
    git fetch remoteName
    This remote branch still different, and in conflict, with your local master branch. And any repo with a remote has this seperate branch but it's normally hidden. You can see it with
    git branch -a

    It turns out that a git fetch is exactly what a git pull does, but then it automatically starts a merge. And, in fact, we could just use git pull origin master instead of git fetch, but then we would be forced into a merge. It's nice to know what's going on before we have to resolve it. Also, doing a fetch is a nice way to check if other developers have pushed changes.
  2. Next, you need to use the same conflict resolution steps listed above to merge the remote branch with your local branch. We can check
    git status

    to see where we are, and we can use
  3. git diff master origin/master
    to see the differences in the conflicting files. Once we feel confident that we can resolve the conflicts, we can use
  4. git checkout master
    to make sure we are on the master branch, then
  5. git merge master origin/master
    which will edit the conflicting files to show the issues, and list them. After we edit the file to consolidate the changes and remove the <<< === >>> tags we can use
  6. git status
    to verify the new files show "both modified" then
  7. git add filename
    to mark the new version to be committed. Finish by using
  8. git commit
    to complete the merge.
  9. git push origin master
    to push the resolved local repo up to the remote.

It's best to collaborate on a branch off of master. When you are collaborating on a project, other people may need to clone and use the master branch for testing or just getting work done. It should be stable and the lead author probably only wants known working code in there. So if you are going to make changes, especially if they are complex, and involved changes in several files, it's best to fork, create a branch, make your changes on that branch (pulling to your local repo, editing, add, commit, push as needed or edit online) and then generate a pull request for the new branch. This gives the lead an opportunity to look at your branch on their local system without changing master.

However, it does make it more difficult to incorporate changes into your branch which are made to master during the period you are working on your branch. Rather than simply pulling and pushing, you need to
git pull
their changes into your local master branch, merge the local master into your branch with
git merge master branchName
resolve conflicts,
git commit

then push your branch to the remote
git push origin branchName
before your branch can be merged into master, either locally or on GitHub.

Erasing Your Changes

If you you decide that everything you tried is wrong, and you just want to toss it and go back to where you were at your last commit, you can issue
git reset --hard
and that will put all the tracked files back to their prior state. It will not remove any new files unless they were added to the staging area.

See also: