FlightGear Git: tips
Git is a version control system, used to store all files required to build FlightGear.
Git is a powerful tool, but can be overwhelming, even to seasoned developers. This page lists some tips and tricks to help you discover all that Git has to offer.
Some more helpful commands
git help [command]
- apply a patch to files and/or to the index http://www.kernel.org/pub/software/scm/git/docs/git-apply.html
git checkout -f
- may be used to throw away any local changes to the working tree. Use with care, as any option that name is -f or --force, and only after reading
git checkout help!
- may be used for future updates
git remote - adding remote repositories
Now, before anyone unnecessarily goes through the pain of cloning fgdata again, you just need to add the git URL to your new personal clone as a remote in your local fgdata clone to be able to push to remote.
git remote add my-fgdata git://git.code.sf.net/p/flightgear/fgdata/
Stores the URL to my fgdata clone under the name my-fgdata in my local git clone of fgdata. git push my-fgdata my-branch:next
Pushes the local branch named my-branch to my-fgdata (i.e. my clone of fgdata) where the branch will be named next.
Switching to a remote branch
You can create a local version of a remote branch with "git branch releases-2.2.0 origin/releases-2.2.0"or "git checkout -b releases-2.2.0 origin/releases-2.2.0". Git doesn't create a local branch automatically. It exists locally as a remote branch. Create a local branch based on it with git branch -t -l my-2.2.0 origin/releases/2.2.0
git pull will also work if you have committed local changes but will make your local history messy (and the official history too if your changes are ever merged back into the official history). If you don't care about the messyness of your local history read no further than point 1 below and use git pull without hesitation :)
If you do have changes you want to keep I'd recommend using git rebase to keep them "on top" of the official work:
1. First commit your changes to your local branch.
git status - show you what files you have modified. git add file1 file2 etc - adds the files you want to commit git commit - creates a commit with the changes you have added.
2. Fetch the latest stuff from the main repository.
3. Rebase your local branch on top of the latest official state. For the FlightGear, SimGear, and FGData sources this would be
git rebase origin/next
4. If you get conflicts you can drop your local conflicting commit by
git rebase --skip
or resolve the conflicts, git add the changed files and continue the rebase with
git rebase --continue
(Use of git status is needed here to see which files are in conflict).
As an additional safe-guard you may create a name for your previous work before you rebase so that you can easily recover it if the rebase goes bad. Assuming your branch is called my-branch the following command creates a back-up point:
git branch my-branch.20110205 my-branch
Merging Topic Branches
You want to merge your branch back into next. merge is the right word:
- git checkout next
- git merge newidea
done. If the merge creates conflicts, git will tell you so. To fix them, simply edit the files and add them to the index (git add fixed_file) and when you are done do a git commit. A merge usually creates a new commit anyway, since it's a new version of your source tree.
If your newidea branch is nice and tidy and a straight continuation of your current next branch (i.e. they have not diverged) you can just merge the newidea branch into next (if they have not diverged it will just be a fast-forward of the next branch).
You can create a new branch to keep track of your old next point first if you like:
git branch old-next next
git branch -h or --help will show many useful options to git branch e.g. for creating, renaming and deleting branches.
As long as you don't rewrite the history you can always create a new branch starting at any old commit so there is no particular need to create such branches before you need them (except maybe to help remembering where that point was).
If the branches have diverged I would consider cherry-pick over the commits from the newidea branch into next (if they are not very many) and perhaps also tidy them up with using interactive rebasing before publishing the new next state. This is particularily useful if next is a public branch that receives commits from other developers - it avoids the rather ugly multiple levels of merges we see in e.g. fgdata. (See also git rebase).
gitk --all is a very useful tool to see where your different branches are in the history and how they relate to each other.
While on the next branch:
git merge newidea
Basically, you have to resolve your conflicts at that point if you want to keep your commit. Even if you merged your changed branch (e.g. with git pull) rather than rebasing it you'd get the conflicts.
- git status to check which files are in conflict.
- git add <files> to register the state you want them to have.This may include cleaning out merge conflict from text files before adding them.use git checkout local-weather -- the/file to restore your version and git checkout next -- the/file to restore the upstream version.
- git rebase --continue to continue the rebase.
For your own local work I recommend committing it in small logical units - that makes it easier to use git rebase --skip to remove local edits when they become obsolete due to upstream updates.
Btw. if you don't have any particular need to checkout the next branch just
git fetch origin/next git rebase origin/next
on the local-weather branch will do.
But do remember to use origin/next rather than just next in git diff and git checkout -- some/file commands in that case, since your local next branch will not be updated by fetch and rebase.
git merge vs. rebase
Each rebase moves your local commits to be on-top of all upstream commits.
For example, my local SimGear branch currently looks like this: git log output:
commit 4087b34f7ebbdb54b62afb205dc2e1ca225dc68b Author: Anders Gidenstam <and...@gidenstam.org> Date: Tue Mar 29 22:44:53 2011 +0200 Experimental Nasal GC work: Added a GC thread. commit d94d1a907d6ec001ab9ba497bc03aaeff55f923c Author: Anders Gidenstam <and...@gidenstam.org> Date: Sun Oct 3 16:59:50 2010 +0200 Turn the creation of a variable without the var keyword an error in Nasal. commit c7c3fae5c2cd21cf81e7a94911568adba926f680 Author: ThorstenB <bre...@gmail.com> Date: Sat May 7 19:40:01 2011 +0200 Also remove visual_enviro.cxx from the VC90 build.
That is my two local commits appear as later than any upstream commit, although they are "older" (as seen by their dates).
This is the effect of the rebase. If I had merged my branch with the upstream branch instead of rebasing it on top of the upstream branch my commits would have been somwhere way down in the history.
However, note that each rebase creates new commits since the commit ID is a consequence of the history preceding the commit. The old "copies" of my commits from previous rebases are no longer part of my local branch.
Keeping topic branches in sync with upstream
Any time someone pushes a change to the remote repository here is the approximate procedure to update my local clones/branches (this is the git replacement for the old cvs update command):
cd "primary-fgdata" git pull <error> - oops I have a branch checked out currently git checkout next <error> - oops commit any changes in the current branch - git diff - git commit git checkout next (try again, it works) git pull (now it works) git checkout "primary-branch" git merge next (to sync the upstream changes with my own "wip" branch)
But this is just in the main fgdata clone, Now cd over to my --local branch clone.
cd "../fgdata-clone" git pull (merge upstream changes from my local next repository that have been merged into the next branch in the previous step.) git diff (see what I changed locally) git commit (commit my local changes) git push (push these changes back into the primary branch in my original clone of the remote repository) <error> - oops I have the branch checked out in my primary local repository - cd "../fgdata-primary" - git checkout next - cd ../fgdata-clone" git push (now it works!)
Woohoo, everything should now be consistent and in sync and all the upstream changes should be fully merged.
Updating a merge request after changes
Once a merge request has been generated from your cloned remote repo, it will be reviewed by a core developer with commit rights. Once the suggestions have been implemented, you'll need to update the merge request with the changes. As core developers always want to keep the git history clean, you'll want to squash your changes into the original merge request patch.
To do this, make your modifications in the same branch you performed the original work in. commit the fixes to your local repo, but before pushing to the remote repository, perform an interactive rebase from the oldest change you wish to commit.
git rebase -i version~1 where version is the sha of the oldest commit to include in the patch
Then push to the remote your updated local repo. You can then choose to update the merge request with the single commit.
Tracking a release branch
git branch -t -l release/2.6.0 origin/release/2.6.0 git checkout -b release/2.6.0
Messed up branches
It's worth experimenting with "git reflog" in situations like this. That tracks a list of HEAD references in strict chronological order (i.e. what has HEAD been in the past, not what commits were done).
In cases where you've completely mucked up the revision history, you can look at this to see what you were doing before, recover the commit ID, and do a reset --hard to that.
"Backing out" is done with git reset --hard last_good_commit. Often the name of the last good commit is HEAD^, the last commit. However, after a botched merge it is good to verify that with git log or graphically with gitk.
A merge commit has two parent commits (leaving aside octopus commits). If you are not happy with the results of the merge, usually you want to revert back to the parent that was on your branch. The reflog can be useful for checking this, but usually the parent of the botched merge on your branch is HEAD^.
If you've pushed a commit to a public repo and then it later turns out that the commit wasn't a good idea, then you want "git revert" which creates the reverse patch for a given commit. However, if you make a real hash out of a public repo you may still want git reset.
Backing it out might be a bit tricky, but you can rename your messed up branch out of the way easily with git branch -m oldname newname.
To cherry-pick commits from your other repository into a branch you first fetch the branch you want to pick, e.g.
git fetch theOtherRepro.git theotherbranch:suitableName/theotherbranch
Or just theotherbranch:suitableOthername
Then you can inspect the commits on it with git log -u theOtherRepro/theotherbranch
And finally cherry-pick the one you want with git cherry pick <commitID>
It's a good idea to always use a clean local copy (e.g. git branch -t mrClean origin/next) of origin/next to cherry-pick commits to before pushing to origin, and leave that branch around since the next time you just need to check it out, do git pull which will be a clean fast forward and cherry-pick and push again.
Resetting the repository
If you find yourself having constant trouble with GIT and being contstantly asked to "rebase" your commitments, please don't delete anything, instead follow AndersG's instructions below.
If you have a local clone of fgdata you have everything at hand - starting over is just creating a new local branch that tracks next, e.g.:
git branch my-new-next origin/next git checkout my-new-next
And update it to latest with (I recommend using --rebase in the future since that will keep your local commits after all up stream commits): git pull --rebase
to investigate what ever uncommitted changes you may have in your tree. If you want to throw all such changes away, use git reset --hard
Or you can use git stash to save them for later.
If you have an old branch with your work and want to reapply selected commits to the new one, git cherry-pick is a useful command.
Updating old FGData clones
If you haven't updated your clone in a while, so that it significantly diverges from the main fgdata repository, pushing all changes would eat up lots of resources (bandwith, CPU) - thus, it is generally better to back up all important data/branches, and then delete your clone, to start over with a fresh clone - that way, you'll save tons of bandwidth and time. Cloning fgdata only takes a fraction of the time that would be required to push hundreds of megabytes of data separately, this is because cloning fgdata is a "local" operation - so that bandwith is not the bottleneck.
Git tutorials and resources
- Git documentation and tutorials
- Git Basics 
- The Git Cheat Sheet and the Git Cheat Sheet Extended Edition
- Egg, a cool Git emacs mode.
- A guide to using Git on Windows
- Git on Windows Go! (Setting up msysgit on Windows)
- qgit - interactive git repository viewer and frontend
- Additional Resources WRT running git on Win32