Monday, March 11, 2013

A quick Git tutorial part 3

Link to a well explained git tutorial
gitref.org

git remote
If you clone a project from another machine, Git automatically sets the remote machine as the origin of the project. So when you want to update with the latest updates of the project you simply do:
git pull

With 'git remote' you can list your remote aliases, so this also means you can add remote machines that has the same git repo:
git remote add alias url
 
This way you can push new changes to a specific machine by giving the remote alias argument, for example:
git push yozef

Of course, you can also remove the alias:
git remote rm yozef

To show the remote list with extra information:
git remote -v

You can change url of an existing alias:
git remote set-url origin git://some.remote.machine/repo.git

To remove untracked files and folders after hard reset:
git clean -f -d

Tuesday, March 5, 2013

A quick Git tutorial part 2

git rm
With 'git rm' we remove a file from the staging index AND from disk. So be careful if you don't want to remove it from disk! If you only want to remove a file from the staging index, you should use:
git rm --cached file

This will remove a file from the staging index and leaves the file on the disk. Sometimes you accidentally added a file you don't wish Git to track it. So the most simple procedure is to perform 'git rm --cached file', than add this file name in the .gitignore file and than finally commit. But what if you have like hundreds of files added in a folder you don't want Git to track them. For this, you can get them out of the staging index like this:
git rm --cached -r folder

Don't forget to add a line in .gitignore file to tell Git to ignore all the files in that particular folder, something like: sources/somefolder/*


git reset
With 'git reset' you actually reset your changes back to the original HEAD (the last commit revision before adding changes). Sometimes you don't know what happened and you get frustrated as everything seems to go wrong. In that case, your last action to the rescue is to go back to HEAD and everything gets wiped out. There is no way in turning back, so be careful.
With 'git reset --hard', the changes in the staging index will be undone and thus completely removed. It's the same as going back to the last commit and no changes are made yet. Notice that HEAD could also be a commit revision. If you want to go back to a previous commit, you could also do:
git reset --hard HEAD^
or
git reset --hard b45ac81

The latter brings you back to the commit with the first seven characters of the sha (in that case ' b45ac81').
If you want to go to a previous commit BUT you want the changes stay in de staging index, than you can do:
git reset --soft HEAD^

With the --soft argument, the changes stays in the staging index.


git stash
Suppose we were editing some files and suddenly a bugfix need to be done. We could create a new branch from the current branch and fix the problem or we can save our work on a stack to get back later. The latter one is called stashing. By performing 'git stash' all the edits will be saved on a stack and you get a message like:
Saved working directory and index state WIP on master: 56490c0 cool stuff with git
HEAD is now at 56490c0 cool stuff with git

You can do some more edits and save this on a stack too. To see what work we have save, we can list them with the following command:
git stash list
result:
stash@{0}: WIP on master: c761ca7 experimenting with git
stash@{1}: WIP on master: 56490c0 cool stuff with git

The top of the stack is 0 and the stack is a LIFO.
WIP could mean Work In Progress, seems logic to me, but it could mean something else.
The master means the master branch, so the message is quite clear.
Anyway, now we want to get the edits back we saved earlier, as we solved a bugfix, this is how we do:
git stash apply
This will get the 'changes' back where we left off. We could also use:
git stash pop
The difference is that the latter one will remove WIP from the stack, while the first one keeps the WIP on the stack. After putting back the saved WIP, we can remove it from the stack with:
git stash drop
This will remove a WIP from the top. If you want to remove them all, you could also issue the following command:
git stash clear


git commit --amend
Oops, you wrote a wrong message for a commit and you want to change that?
git commit --amend -m "my new message"


git cherry-pick
This commands can be useful if you want to add a commit from another branch to your master branch. You can imagine that while you're working on a separate branch, you already want to commit some of the work already done and not waiting until you finished your big changes on the separate branch.
We can do this by 'picking' a commit from another branch and 'insert' it in the master branch.
So suppose we are in a branch called 'test' and we've already done some commits and we're still busy doing some edits in some files. We can merge the last commit that's already done to the master branch. So we switch to the master branch:
git checkout master
Than we pick a commit, i.e. '93e8ab7', from the 'test' branch and insert it in the master branch like this:
git cherry-pick 93e8ab7

You can do this multiple times, but chances are you'll get conflicts if the same files will be committed again. To solve this, we 'git rebase' the branch on to the master branch. It's like making a custom branch compatible with the master branch. You will see that the commit hashes are changed after a rebase. From there on, you can switch to the master branch and merge the commits from your custom branch to the master.

To rebase, you go to your custom branch and do the following:
git rebase --onto master 1234abcde^
where 1234abcde^ is the commit from where you want to rebase to the master branch.
After that, you'll notice that commit sha's from 1234abcde to the last commit, will be changed, so it fits to the master branch. Now, if we switch back the master branch and do: git merge custombranch
Than all the changes will be merged to the master branch.
Play with it, cause it's a bit confusing and you better should avoid cherry-picking if not necessary.

git format-patch, git apply
When developing with a team on a project, there is often the case that you have one central source repository (based on Git) where the latest source codes maintains. It would be a mess if all developers of the team starts pushing the new codes and bugfixes. To keep the situation organized, there should be some kind of source code maintainer who checks the new codes and bugfixes before it updates the repository. What you get is developers are forced to send patches of codes to the maintainer and the maintainer checks whether it's ok or not, this way you get a controlled process of developing on a project with a team.
Well, to create a patch in Git, it's best to create a seperate branch and do your editing there. If you made a little fix, or added a new feature and you tested well, you finally create a patch, to do this you do as follows (assuming you're on a different branch):
git format-patch master
This means, make a patch from the difference between master branch and the current branch.
We could also do this:
git format-patch HEAD^^...HEAD
This means, make a patch from two previous commits ago to the last commit.(^  means previous).
We could also use commit revisions instead of HEADs.
Often you want to give a patch a name, so others have an idea what the patch is about:
git format-patch master --stdout > fix_bug_123.patch

On the other side, if you want to apply a patch in your master branch, it's just simply this (assuming you're on the master branch):
git apply fix_bug_123.patch

Understanding difference between cherry-pick and merge
When you have a custom branch to do some edits on it, you might have made multiple commits. So when you're satisfied with the final result, you want to merge it to the master branch. So you switch to the master branch and do 'git merge custombranch'. What happens is all the commits are copied to the master branch, you can see them with 'git log --oneline'. If you want to cherry-pick a commit, you actually want to copy the changes of one commit to your master branch (or whatever branch you are). So you get a new commit based on the 'changes' of your custombranch which is a different hash!
I'll add some images to get a better understanding, as there is a saying "a picture says more than thousand words".

git diff
To show what files have changed, 'git diff' shows the content of the changes, for example:
git diff
this will show the difference between the last commit and the added changes that are NOT added in the staging index
To show the changes in the staging index, do this:
git diff --cached
If you want to show both of the changes, meaning the changes in your working directory, do this:
git diff HEAD

To show the difference between two commits:
git diff HEAD^ HEAD
Note that the HEAD's could also be the sha's of the commits.




NSNotification example