From CS 345 Wiki
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
- Mac OS X - use git-osx-installer 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.
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 now has a host of options for the project relating to Git operations. Note, some things you will still need to do from the command line as the plugin is still in early development.
will open up a graph of your branches and commits.
will show the graph of your whole branch structure.
will open the git gui.
will show a ascii graph of your branches and commits in the terminal/shell.
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 myDev branch and master branch up to the current revision HEAD in the upstream repository (i.e., origin):
$ git checkout master $ git pull $ git checkout myDev $ git rebase master
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 -b <bname> master $ git merge --squash --no-commit myDev
The merge command applies all of your revision history to the <bname> 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 submitting patches.
To format a patch for submission:
$ git format-patch origin..HEAD
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. You can verify your patch easily too by applying it to the master branch. If it applies, compiles, and runs correctly, then the patch is good:
$ git apply <patch-name>
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
You can verify your patch by applying it again to the same upstream branch that contains the last commit from the origin repository.
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 config 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.
Conflicts may arise when merging two branches or rebasing your work on an updated branch. A conflict is when Git finds modifications in the same area of the file and cannot resolve which modifications to preserve. When a conflict occurs, Git turns to you, the user, to resolve the conflict. For example, is a conflict detected from a rebase on the master branch after new changes had been pulled into the master branch:
$ git checkout foo Switched to branch 'foo' $ git rebase master First, rewinding head to replay your work on top of it... Applying: Modified fact Using index info to reconstruct a base tree... Falling back to patching base and 3-way merge... Auto-merging fact.txt CONFLICT (content): Merge conflict in fact.txt Failed to merge in the changes. Patch failed at 0001 Modified fact
When you have resolved this problem run "git rebase --continue". If you would prefer to skip this patch, instead run "git rebase --skip". To restore the original branch and stop rebasing run "git rebase --abort".
Notice that Git gives a few courses of actions. We are going to resolve the problem, and then have the rebase continue. The status commit identifies files with conflicts:
$ git status # Not currently on any branch. # Unmerged paths: # (use "git reset HEAD <file>..." to unstage) # (use "git add/rm <file>..." as appropriate to mark resolution) # # both modified: fact.txt # no changes added to commit (use "git add" and/or "git commit -a")
Whenever a file's status is "both modified" the files contains a conflict. The term both modified indicates that the changes from both branches on the merge conflict and cannot be automatically resolved by git. You need to edit each file with a both modified to resolve the conflict but choosing which changes to accept (or by creating a hybrid of the two changes). Conflicts are delineated by <<<..., ===..., and >>>... character sequences indicating differences in the two branches involved in the merge and more than one conflict may appear in a file. For our example, the fact.txt file includes the following annotations:
<<<<<<< HEAD CS 345 Rules always. Really! ======= CS 345 Rules sometimes. Really! >>>>>>> Modified fact
which is resolved as
CS 345 Rules sometimes or always. Really!
Once the conflicts are resolved in the file, add the file to the index to inform Git of the resolution. If the conflicts came from a rebase, then continue the rebase.
$ git add fact.txt vache:sample egm$ git rebase --continue Applying: Modified fact
Repeat the process as often as conflicts arise. If the conflicts came from a merge, simply fix the conflicts and commit the result. The message from Git is
$ git merge master Auto-merging fact.txt CONFLICT (content): Merge conflict in fact.txt Automatic merge failed; fix conflicts and then commit the result.
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 is 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>