From CS 431
Distributed version control is the way of the future and certainly the growing way of the open source community. The power of distributed version control is the ability of you, the developer, to track and manage your own development without affecting other developers. In other words, you can obtain, for example, source code from an existing project and begin modifying the code and tracking your modifications in version control without any of your modifications being visible to other developers. This version control includes adding, deleting, branching, merging, tagging, etc. While doing all your individual version control, you can still track, update, and merge changes from the original code into your development tree. Once you reach a point where you are ready to show your code to others, you can fold all of your revisions into a single commit on the repository you used to start your development (or even a different repository for that matter). This decentralized development is what we mean by distributed version control.
For this course, you will use Git to get the programming projects, merge in changes when appropriate, track your own version control, and create patches to submit for your solutions. Git is the center of the larger programming projects in the course for distribution, patching, and submission of solutions.
- Windows installation using mysysGit
- Git Mac OS X installation
- Mac OS X - use MacPorts RECOMMENDED
- cogito is a nice front-end to Git that is less harsh for some folks.
We are using both Subversion and Git in the course. It turns out that there is a Eclipse Git Plugin if you are interested, although it is not required or necessary. Installation is a little tricky but is greatly expedited with Chris Cruft's helpful blog entry.
To use the Git eclipse plugin do the following after it is installed:
- Clone the repository of interest
- Import the cloned repository as a project in Eclipse
- Click on the imported project, right click, select Team, and Share project...
- In the Share Project window, choose Git
- In the Configure Git Repository window, select Search for existing Git repositories and choose Finish
The Team menu know has a host of options for the project relating to Git operations. Note, somethings you will still need to do from the command line as the plugin is still in early development.
Cloning creates a local copy of a repository, including all of the repository's revision history. As an example, to clone the repository that contains the lectures for this course, use this command:
$ git clone <user>@cs.byu.edu:~cs431ta/cs431-files/cs431-lectures Initialized empty Git repository in /Users/egm/tmp/cs431-lectures/.git/ <user>@cs.byu.edu's password: remote: Counting objects: 133, done. remote: Compressing objects: 100% (133/133), done. Indexing 133 objects... remote: Total 133 (delta 78), reused 0 (delta 0) 100% (133/133) done Resolving 78 deltas... 100% (78/78) done
Please note that <user> should be your username. Also, some versions of Git do not like the ~-expansion so you might need to include the full path after the ":" character.
Right after cloning a repository that you plan to modify (like a repository for one of the milestones), it's a good idea to create a branch. A branch lets you keep track of your changes separately from the main branch. When you're ready to submit, you can flatten and merge your changes into the main branch. I always recommend working on an branch and leaving master untouched.
Creating a branch
To create a branch and switch to it:
$ git checkout -b <branch_name>
<branch_name> can be whatever you want, like devel or mydogspot.
To create a branch without switching to it, use this command:
$ git branch <branch_name>
You can see a list of all of the branches with git branch, like so:
$ git branch master milestone1 * upstream
The currently selected branch is marked with a *. Use git checkout to switch to a different branch.
Switching between branches
To switch branches, use git checkout:
$ git checkout master Switched to branch "master" Your branch is ahead of 'origin/master' by 21 commits. # Note that the current branch (denoted by a *) is now "master" $ git branch * master milestone1 upstream
Flattening Revision History
When working on a feature in an existing code base, you'll probably want to commit often to keep track of your progress. However, when you finish your feature and you're ready to send a patch to an upstream repository, you probably won't want to send them all of your revision history including your commits, lame comments, and bad choices. So the question becomes, how do you coax Git to generate a single commit (patch) for an upstream repository with an appropriate comment when your current revision history is riddled with many commits? The answer depends on whether you did your work in a branch, as suggested above.
If your changes are in a development branch
First, bring your devel branch and master branch up to the current revision HEAD in the upstream repository (i.e., origin):
$ git rebase origin
git rebase first unwinds your commits to the branch point, applies all the changes form matching upstream branch, and reapplies your commit history marking conflicts as they arise. For each conflict, the rebase stops to allow you to resolve the conflict before moving to the next commit to apply.
Once you have fixed any conflicts etc. to synchronize with any changes on the master branch that occurred after you branched devel from master, then you are ready to flatten your revision history into a single commit.
$ git checkout master $ git merge --squash myDev
The merge command applies all of your revision history to the master branch, but it does not perform the intermediate commits on the revision history. In other words, it makes all of the changes, but commits none of the changes. Again, fix any conflicts that happen during the merge. Once you have fixed conflicts, you are ready to perform a single commit with all of your changes:
$ git commit -a -m "Added new feature ...."
From here you can create a patch or push upstream depending on the rules for the origin. For this course, you'll be creating and emailing patches.
To format a patch for emailing:
$ git format-patch origin..HEAD
As a note, you actually do not even need to do the commit if it is not desired. The roll back to master is easy if needed. On the master branch, do
$ git-checkout -f
Regardless, all your work is rolled into one single change that you can either commit, use to generate a patch, or toss out completely. In any case, your development branch and revision history is preserved.
If your changes are on the master branch
If you made your changes on the master branch, you'll have to create a new branch that's synced to the latest upstream commit, and then apply your changes to the branch.
# pull the latest changes from the upstream repository and merge them into the local copy $ git rebase origin # list commit history git log # look through log results until you find the latest upstream commit; note its id # create a new branch named "upstream", branched from the last upstream commit, identified from the log git branch upstream <upstream commit id, e.g. d1bdb9e7dea3099cd2466edd2fe45af1e55c2a11> # Switch to the new, clean branch git checkout upstream # Merge and squash everything from master into the clean branch git merge --squash --no-commit master # Commit the merged changes git commit -a -m "Your log message here" # Create a patch file git format-patch origin..HEAD
End-of-line encoding is always an issue between Microsoft and *NIX based systems. Windows prefers CRLF where *NIX prefers LF. The safest route is you are on a Microsoft platform is to issue the following in your repository:
$ git confit core.autocrlf input
In this mode, Git converts the files on input, but writes out the files as LF. The git config manpage has more details and options.
Recipe To Follow
This section gives a simple recipe to follow in using git for the course, and it is the same recipe that you will find in the git-for-dummies.pptx lecture. I strongly encourage you to move beyond the recipe to appreciate the full power of git, but for now, it is good place to begin. The recipe starts assuming you have cloned the kernel repository.
To Start A Project
From the master branch:
> git checkout -b <project-name>
To Commit Changes
Whenever you want to mark changes into your local repository:
> git add <filename> > git commit
You can add the -m flag to git commit to supply a message:
> git commit -m "My message"
Also, as a short cut, a -a flag adds all tracked files to the index (it does not add new untracked files):
> git commit -a
Finally, you can use the -F flag to specify a file containing your commit comment:
> git commit -F <filename>
To Make a Patch
When it time to create and submit a patch, first synchronize with the master branch with the origin/master:
> git checkout master > git pull
Rebase your changes onto the master branch if it has moved (not likely):
> git checkout <your-project> > git rebase master
Check out a new branch to flatten your changes into a single commit. Merge in your changes and create the patch:
> git checkout -b <submission> master > git merge --squash --no-commit <your-project> > git commit > git format-patch origin..HEAD
Check your Patch
Assuming you followed the recipe and did not modify the master branch:
> git checkout master > git apply <patch-name>
Cleaning up after checking a patch
The best way to get rid of modified files is the following:
> git checkout -f
Also, if you want to delete all untracked files and directories:
> git clean -f -d
Unstaging a file
If you accidentally added a file to the index (git add) and you wish to unstage it:
>git reset <filename>