path | title |
---|---|
/learnings/git |
Learnings: Git |
Git is pretty neat, and lets you think about source code management in totally different ways. Go Git!
a complete (approximately) copy of the repository on your local machine.
This is disconnected with the server's version of the repository until you push or pull
A remote copy of the code, on something that behaves like a server. (Github, Bitbucket, or a machine you have git ssh into to store changes. Git is super scale-downable.)
Can be named whatever, but by default your first remote is named "origin".
These are easy to ceate. Unlike subversion Git does not use the file system to store different branches, and because everything is distributed you ca create a branch on your local machine, and it doesn't exist anyhere else (until you push it)
Commit. These are also easy to create and commits can exist just on your local machine. Because of thi I like to make my Git commits very small. I like to aim for my commits to feel like a single thought: almost a sentence, as if it was code.
Does this commit do one thing? No, seriously, one thing. I may write some code for a ticket or task and break it up into 5 or 6 seperate commit yjust committing the right parts. Theres a bit of an art form here, and also it might not matter if you squash your merges, but it makes me feel good.
In Git commit numbers are SHA1 summaries of the content (they are not sequential ie as in Subversion or CVS). Additionally, in most cases only the first 7 characters of a SHA-1 matter to uniquely identify a commit (unless you're the Linux kernel, which needs 8 due to the sheer size of the codebase).
Unlike Subverion, the whole woking copy is not automatically included in a commit. You have to add files to the stage, then commit them.
Some Git UIs may ignore parts of this, but because o how it work (splittig work into sentences) I see this as a feature, not a bug.
Git doesn't work on just a file level: you can stage indidivual parts or lines of a file. SourceTree does this really well, the git CLI you have to be clever about.
Push: send the code you have to the server
Pull: pull the code on the server down to you.
If you are pushing a feature on a branch you are working on with a friend, and they have committed to the repository, you'll need to pull
to get their changes. If that happens and you have changes on your end, Git will probably figure out the merge and do it corectly (unless you literally change the same lines). Only then can you push
your changes to the remote.
download all the branches from the remote repository. Usually not required, but if you get an error from git about it can't checkout a branch you know exists, do a fit fetch.
puts all your changes in a temorpary holding area, and clears the working directory. This may allow you to switch branches when you for example realize that some of your work belongs in another branch.
There's a couple different ways you can do this. One can set it up so that merges of pull requests will condense all the commits in your pull request into one, clearly labelled, commit. This is called a squash merge.
$ git checkout -b BRANCH_NAME_THAT_YOU_WANT_LOCALLY --track origin/REMOTE_BRANCH_NAME
Which will pull down the remote branch (found at origin/REMOTE_BRANCH_NAME) into the locally named branch BRANCH_NAME_THAT_YOU_WANT_LOCALLY.
(Technically there's nothing special about origin - it's just the name of the remote tracking branch you're using. This is explained later...)
Then git pull to get the code...
(aka: pull a commit from the trunk into a release branch)
Use git-cherry-pick to "Cherry-pick" ''just'' one commit out of a branch to put it in the current checkout:
$ git cherry-pick -n SHA1
(Replace SHA1 with the SHA1 hash for that commit you see in git log)
When you're feeling confident you can remove the -n training wheels. That just says "Do not automatically commit the cherry pick".
Now your work is on your local machine, but you have to move it up to the remote server's branch.
$ git push origin BRANCH_NAME_THAT_YOU_HAVE_LOCALLY:REMOTE_BRANCH_NAME
By default a "git push origin" command will post your changes up to the remote server to origin/BRANCH_NAME_THAT_YOU_HAVE_LOCALLY, which you might not want. So we add the LOCAL:REMOTE parameter to explicitly state what branch it goes to remotely (neither LOCAL nor REMOTE parameters need origin/. If your remote branch is origin/release1.0, all you need is the release1.0 part)
You want to make a new branch
$ git push origin BRANCH_NAME:NEW_BRANCH_NAME
$ git push origin BRANCHNAME
ksh: git-receive-pack: not found
This error is caused by the current machine (the push-er) not being able to access git-receive-pack on the remote machine (the pushed-to) because git-receive-pack isn't on the PATH for the user the client logs in under.
'''Solution''': figure out what the path for git-receive-pack on that remote machine and give it to git explicitly like so:
$ git push origin BRANCHNAME --receive-pack=/opt/local/bin/git-receive-pack
....
$ git checkout -b wreal_soft_launch_2008121 --track origin/soft_launch_2008121
fatal: git checkout: updating paths is incompatible with switching branches/forcing
Did you intend to checkout 'origin/soft_launch_2008121' which can not be resolved as commit?
'''Solution''':
$ git fetch
I think this problem is related to git not having the remote branch names on hand, so a git fetch gets those and lets you check them out... I think
See also: [http://www.skrinakcreative.com/wp/?p=175 This problem at skrinakcreative]
Pulling changes in the master up into your current branch (in your branch):
$ git rebase master
Pulling changes in the branch to the master (from the trunk/master branch)
$ git merge branchname
You can create a git branch on top of a dirty working copy and have a branch created with those (dirty) changes.
Commands like git-pull and git-push are in the following format:
$ git-pull REMOTE-NAME BRANCH-NAME
When you clone a git repo, git-clone actually creates a remote tracking branch (named "origin", by default) and checks out the initial branch equal to the remote's currently active branch. (http://www.kernel.org/pub/software/scm/git/docs/git-clone.html Reference) (Yes, this is dense English). BUT you can use git-clone -o NAME ... to override the default NAME of "origin". Or you can let git-clone to its thing and edit .git/config yourself with the new name.
By default git-pull and git-push know what remote tracking branch you're on now, and what regular branch, but if you want to be pedantic (or want to override something for some reason) that is the syntax. See the "pushing your changes up to Git-hub" section for an example.
But these optional parameters mean you can be tracking '''many''' remote branches at one time... like tracking the main git remote branch, and also a (say) remote branch on your desktop machine for the work you've done there but not pushed up to the main remote branch yet.
- git stash / git stash apply <-- "get my unsaved changes out of the way, so I can push or pull/OK, put my changes back"
- git checkout FILE <-- will revert any modifications to FILE made since the last commit (so it's like svn revert FILE)
Git is distributed - which means there doesn't have to be one centralized server. One (very low-tech, but very simple) way to do this to just email git diffs back and forth - telling people exactly what you changed in one commit (or a series of commits, or whatever). Which might just be Good Enough For Right Now... or good enough for your situation.
To package your changes up:
$ git commit #commit your changes to the repo
$ git-format-patch HEAD^
The git-format-patch command says ("package the differences from the last revision to HEAD up in mailbox-style format). (Check out the rubinius link below for what the ^ means)
Now send your email away
On the receiver side:
$ git-am MAILBOX_FILE_YOU_GOT_VIA_EMAIL
Which imports the commit you got via the mail... and commits it to your repo. It'll look (in git-log) like ''they'' made the commit!
Thanks to the [http://rubinius.lighthouseapp.com/projects/5089/using-git Rubinius documentation on git] for help with git-format-commit!
NOTE: I wouldn't do this for any real length of time, nor with more than say 2 developers... but it might just keep your dev team truck'n when the central server dies.
If errors about whitespace, see this blog article
git-svn is our preferred way to use Subversion
git-svn doesn't have native support for svn:externals. But there are a number of third-party tools to fill the gap:
- git-svn-clone-externals It makes you put the checking scripts in the same folders as the externals (making it a PITA that way), BUT it includes nice utility scripts that check to see if you have any unpushed changes/uncommitted changes OR if your externals are out of date.
- git_svn_externals Has differences from the above script in the following ways: (1) will recurse the directory and find folders with svn:external properties (instead of needing you to place the script correctly) (2) will update all these external repositories when ran at the top level directory again and (3) is compatible (similar directory structure and approach) to the git-svn-clone-externals script above (meaning you can use the more advanced git-svn-check-unpushed and git-svn-externals-check scripts from the former.
- gsc Python utility that does the same. Disadvantage is that it bakes in assumptions about trunk/branches/tags, which may not always be the case with my projects.
SVN Branches in git. In a nutshell: tell git svn clone where the trunk and branches directory is (or use git svn clone $URL --stdlayout). Then read the article.
$ git diff
$ git commit -a -s # -a = sync index and (??) -s = sign off on changes
$ git push origin master # send thosue changes up to github
= Git Documentation / Tutorials =
A Git introduction by the CS department at cmu.edu
(some parts specific to their setup, but after the first "installing git" part it's good)
GitMagic is a great bit of git documentation!
Git Guide for SourceMage (in Q&A format)
= Useful Third Part Git Tools =
Git-wtf: gives information on how your current repo differs from the remote repo, and the merged state of your branches
Sequential Revision Numbers in Git
Merging can be, umm, fun.
$ git checkout --ours somefile.txt OR $ git checkout --theirs somefile.txt
Get rebase for picking apart commits explained
Removing a commit in an un-pushed repo
( create your new branch, or get on master or whatever)
$ git cherry-pick -e SHA1
$ git reset HEAD~1
Now you are at a point where you would normally git add files to the commit. Now craft your commit with git add -p or GitX or whatever, and make your commits.
$ git branch experimenting
$ git reset --hard HEAD1 # HEAD1 should be the commit you want to move over
(Thanks to this blog entry: Using Git To Pull in A Patch From A Single Commit
git fetch REPO branchname #where REPO can be the location of an on-disk repo, or a 'remote' name git cherry-pick SHA1
http://www.elctech.com/tutorials/untangle-your-git-commits-with-git-add---patch how to add individual chunks from command line/interactively
Arild Shirazi said, on this front:
$ git add -p foo
$ git commit foo
is WRONG
the right way to do it
$ git add -p foo
$ git commit
as soon as you specify a directory of file for the commit, it ignores the partial (hunk) to commit, and goes ahead and commits the whole file
== Integrating Git information into builds == Git and XCode build script (also has alternative versions and a Hg shell script too!)
See also: