On Github catalyst-training / git-basic
You (should) want it if...
You need it if...
# Required $ sudo aptitude install git
# Recommended $ sudo aptitude install gitk git-gui
# FYI... $ sudo aptitude search gitPause here to explain the way we are showing commands * talk about '$' as the prompt, etc
For MS Windows:
Once it is installed, you need to find "Git Bash" in your start menu
See also: https://github.com/catalyst-training/git-basic/wiki/Windows
git init
$ mkdir /tmp/test $ cd /tmp/test
$ git init Initialized empty Git repository in .git/
Check the status with git status
$ git status # On branch master # # Initial commit # nothing to commit (create/copy files and use "git add" to track)
Git is just a bit different from other revision control systems
The 'staging area' is often called the 'index'# git help COMMAND $ git help status # man git-COMMAND $ man git-statusThe COMMANDs we have used so far are 'init' and 'status', so....
Before we start...
# We need to tell git who you are - But it's OK to lie.... $ git config --global user.name "Jed I. Night" $ git config --global user.email "null@catalyst.net.nz"
# You can choose an editor.... (this is not your IDE) $ git config --global core.editor gedit $ git config --global core.editor wordpad
# If you will be working on windows, but need to deal with unix $ git config --global core.autocrlf trueIs anyone using windows? you can't use gedit, and you don't want to use notepad, try: - notepad++ - wordpad - ANYTHING else! - but only within the current repository (which is /tmp/test)
This will tell Git you like to see pretty colours on the screen
$ git config color.ui autoThese settings will override the global settings - but only within the current repository (which is /tmp/test)
Global data is stored in ~/.gitconfig
Project data is stored in /tmp/test/.git/config
# Use `git config` to see it $ git config -l
# ..or just edit the files $ gedit ~/.gitconfig /tmp/test/.git/config
Make some changes...
$ touch myscript.pl
$ git status # On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # myscript.pl nothing added to commit but untracked files present (use "git add" to track)
git add
$ git add myscript.pl
$ git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: myscript.pl #This actually adds the file to the 'index'
Make some changes...
$ echo DATA > myscript.pl
$ git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: myscript.pl # # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: myscript.pl #This is our first clue that Git tracks changes rather than files
git diff
$ git diff --- a/myscript.pl +++ b/myscript.pl @@ -0,0 +1 @@ +DATA
We need to tell Git that you intend to commit the checked out version, with git add
$ git add myscript.pl
$ git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: myscript.pl #
Commit your work with git commit
# You will be stuck in an editor so you can input a commit message. $ git commit myscript.pl Created commit ae54011: Add a message. 1 files changed, 3 insertions(+), 0 deletions(-) create mode 100644 myscript.pl
Normally, you WILL make your commit message meaningful!
Check if everyone got a reasonable editor! Windows problems can be resolved by looking at https://github.com/catalyst-training/git-basic/wiki/Windows-editorYou don't have to git add all your changes manually
# Add <file>, and then commit $ git commit <file>
# Add all your changes, and then commit $ git commit -a
# You can choose different editor for this commit... $ EDITOR=vim git commit -a
# You don't have to use an editor at all... $ git commit -m 'commit message goes here'
Oops, that file isn't executable
$ chmod 755 myscript.pl
$ git commit myscript.pl Created commit 378af93: Make it executable. 0 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 myscript.pl
To rename a file use git mv
$ git mv myscript.pl README.txt
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # renamed: myscript.pl -> README.txt #
# This has changed our checkout, and the index # so we are ready to commit the change $ git commit Created commit fabd446: Rename the file. 1 files changed, 0 insertions(+), 0 deletions(-) rename myscript.pl => README.txt (100%)
To delete a file use git rm
# This won't actually work, because we haven't # created "another-script.pl" # ... but you can try it, if you like.... $ git rm -f another-script.pl
$ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # deleted: another-script.pl #
And commit the change
$ git commit Created commit bd10c5f: Remove another-script.pl 0 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 another-script.pl
# Let's imagine your editor keeps creating '.swp' files $ touch README.txt.swp
$ git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # README.txt.swp
# Add a line that conatins '*.swp' $ gedit .gitignore
$ git status # On branch master # Untracked files: # (use "git add <file>..." to include in what will be committed) # # .gitignore
# We can then commit the .gitignore file $ git add .gitignore $ git commit -m 'Created a .gitignore file'You can put a different .gitignore file in each directory ...or stick to one in the root of your project
Tags allow you to mark specific points in history as being important.
Often people use this functionality to mark release points.
They work pretty much like you would expect...
$ git tag v1.0 $ git tag v1.0-production -m 'this might be important later!'
$ git tag $ git show v1.0 $ git show v1.0-production2 types of tags: lightweight and annotated Tags don't get shared, by default. Also, explain that you can git show anything (tag, branch, sha1, etc).
Use git log
$ git log commit 09f90bc2a74f7ca2f825c4a386e168649b9fec97 Author: Evan Giles Date: Thu Dec 16 11:53:48 2010 +1300 Rename the file. commit f7123c5ffdccbed11b50b0b74a181186e1a13028 Author: Evan Giles Date: Thu Dec 16 11:53:25 2010 +1300 Make it executable commit 8657bea3230587abdcbe39e6bf699c9ee8f505a6 Author: Evan Giles Date: Thu Dec 16 11:52:51 2010 +1300 Add a message.
By default the output is piped into $PAGER
# You can change the output format $ git log --prety=onleline
# Or specify which file or directory you are interested in $ git log README.txt
# There are lots of options.. $ git log --pretty=oneline --since='5 minutes ago' --graph
Show who made changes to what line
$ git annotate filename.pl b61863c8 (Andrew R. 2008-05-09 17:01:41 +1200 1) #!/usr/bin/perl -w b61863c8 (Andrew R. 2008-05-09 17:01:41 +1200 2) b61863c8 (Andrew R. 2008-05-09 17:01:41 +1200 3) print "silly script\n"; 9c08faf4 (Bad Coder 2008-05-09 17:18:18 +1200 4) I'm in ur code, breaking ur stuff.
Who is this Bad Coder?
# Better remembered as `git blame` $ git blame filename.pl b61863c8 (Andrew R. 2008-05-09 17:01:41 +1200 1) #!/usr/bin/perl -w b61863c8 (Andrew R. 2008-05-09 17:01:41 +1200 2) b61863c8 (Andrew R. 2008-05-09 17:01:41 +1200 3) print "silly script\n"; 9c08faf4 (Bad Coder 2008-05-09 17:18:18 +1200 4) I'm in ur code, breaking ur stuff.
Not strictly change logs, but... gitk / qgit / gitg / etc
To show branches use git branch
$ git branch * master
Only one branch, called master
The '*' indicates that it is your current branch
You can also use 'git status'Use git branch (or git checkout -b)
$ git checkout -b new-branch Switched to a new branch "new-branch"
Will create "new-branch", and check it out for you
$ git branch master * new-branchmention that 'git checkout' is a convenience interface ...it does several things in one step
By default a new branch will be created at the commit you already have checked out
You can also create a new branch at another commit
$ git checkout -b another-branch master Switched to a new branch "another-branch"
$ git branch * another-branch master new-branchHere, the checkout command is actually doing 3 things - go the the master branch - make a branch there (callled 'another-branch') - checkout the 'another-branch' branch
Use git checkout
$ git checkout new-branch Switched to branch "new-branch"
$ git branch another-branch master * new-branchDon't forget to see how this is shown in gitk
# Make a change to a file (and commit that change) $ echo "... From new-branch" >> README.txt $ git commit README.txt Created commit a6ed2cf: From new-branch 1 files changed, 1 insertions(+), 0 deletions(-)
# Check the file.. $ cat README.txt DATA ... From new-branch
# Switch to a different branch (master) $ git checkout master Switched to branch "master"
# The text we added ain't there $ cat README.txt DATA
You should now be on the master branch
$ git status # On branch master nothing to commit (working directory clean)
Use git merge
$ git merge new-branch Updating e766bfb..a6ed2cf Fast forward README.txt | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
If there are no conflicts, this may make a new commit object
but in this case, we have done a fast forward merge
- We are merging changes IN, from somewhere else - explain the fast forward bit - talk about merges being line by line, so not for binary data - use gitk to see what happenedConflicts are more difficult (lets create one)
# Make a change to a file, commit that change to 'master' $ echo "New value for data" > README.txt $ git commit -a -m 'update script with a new value' # Change branches, make a change, and commit $ git checkout new-branch $ echo "different value for data" > README.txt $ git commit -a -m 'update script with a different value' # Go back to the master branch, if we try to merge - there # will be a problem! $ git checkout master
If there is a conflict then git will stop and make you fix it.
$ git merge new-branch Auto-merged README.txt CONFLICT (content): Merge conflict in README.txt Automatic merge failed; fix conflicts and then commit the result.
$ git status # On branch master # Unmerged paths: # (use "git add/rm <file>..." as appropriate to mark resolution) # # both modified: README.txt # no changes added to commit (use "git add" and/or "git commit -a")
$ cat README.txt <<<<<<< HEAD:README.txt New value for data ======= different value for data >>>>>>> new-branch:README.txtMention git merge --abort
Lots of tools to help fix things up. Hand edit, xxdiff, smartmerge, etc
We'll just fix it by editing the file
$ gedit README.txt $ cat README.txt New, different value for data
But Git still won't allow us to continue...
$ git commit U README.txt fatal: 'commit' is not possible because you have unmerged files. Please, fix them up in the work tree, and then use 'git add/rm <file>' as appropriate to mark resolution and make a commit, or use 'git commit -a'.
We need to tell Git that it has been fixed before we commit
$ git add README.txt $ git commit Created commit a411f2a: Merge branch 'new-branch'
In this case the commit message is automatically created, but you can edit it if you want.
Merge branch 'new-branch' Conflicts: README.txt
Looking at the trees in gitk helps you understand stuff.
$ gitk
We already know how to create a new repositry
$ git init Initialized empty Git repository in .git/
...but normally, you want to join in with an existing project
In practise, you're more likely to use git clone.
To check out the source code of a random project:
$ cd /tmp
$ git clone https://github.com/catalyst-training/git-basic Cloning into git-basic... remote: Counting objects: 90, done. remote: Compressing objects: 100% (43/43), done. remote: Total 90 (delta 44), reused 90 (delta 44) Receiving objects: 100% (90/90), 31.62 KiB, done. Resolving deltas: 100% (44/44), done.
$ cd git-basic
$ ls
$ gitkgit remote -v
# Clone an existing repository via HTTP $ git clone https://github.com/catalyst-training/git-basic
# Clone an existing repository via SSH $ git clone ssh://git.catalyst.net.nz/git/public/git.git
# Clone an existing local repository (this is only slightly mad) $ git clone test test2
Remote branches are references (pointers) to the state of branches in your remote repositories.
They're local branches that you can't move; they're moved automatically for you whenever you do any network communication.
Remote branches act as bookmarks to remind you where the branches on your remote repositories were the last time you connected to them.
You can see all branches (including the remote ones) with the git branch command
$ cd /tmp/git-basic/ $ git branch -a * master remotes/origin/masteryou don't need to type 'remotes' to refer to these branches
You can manipulate your view of other repositories with the git remote command
$ git remote add duplicate https://github.com/catalyst-training/git-basic $ git remote show duplicate origin
$ git fetch --all Fetching origin Fetching duplicate From https://github.com/catalyst-training/git-basic * [new branch] gh-pages -> duplicate/gh-pages * [new branch] master -> duplicate/masterorigin is not special, except that: - it is created by default when you clone a repository - some commands (like fetch) assume you mean origin if you don't specify the remote - etc, etc
You will probably use this
A quick diversion: http://www.github.com
This is how the Linux kernel is managed
Once upon a time....
Alice and Bob wanted to do some work
So they both made some changes....
Alice pushes her changes...
But Bob has a problem...
Now Bob needs to fetch the changes from Alice
and merge them with his work
They all lived happily ever after...
# Clone a new repository $ cd /tmp $ git clone https://github.com/git-training-open/alice1.git $ cd /tmp/alice1
# Create a new file (and commit) $ gedit $YOURNAME.txt $ git add $YOURNAME.txt $ git commit
# Push your changes to the shared repository # git-training-open / openup1 $ git push origin master
# You are now Bob! $ git fetch $ git merge origin/masterExplain What is going on with "origin" and "master" here: - git push origin master - git merge origin/master Also introduce the idea that commands have defaults: - git push ... git push origin master - - -
You can 'change' the most recent commit using git commit
$ git add something.txt $ git commit --amend
This is quite a big deal...
Will someone please pull me up on this?! Explain how this works - That you are not changing anything here - just making a new commit which is quite like the old one - and then moving the labels around Warning: - Do not change commits that exist outside your repository. - If you do, people will hate you, and you'll be scorned by friends and family.You can copy a commit from one branch to another
You might want to do this if, for example, a bug is discovered on your development branch that also needs to be applied to your production branch.
$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdfThe commit created will look the same, but have a different commit id Note that the cherry-pick can fail, if there are conflicts
You can undo a previous commit with git revert.
This makes a new commit, that is the exact opposite of a previous commit in your repository.
The previous commit remains intact.
$ git revert e43a6fd3e94888d76779ad79fb568ed180e5fcdf
$ git log --pretty=oneline 830d77273fc24c0708403102a05a5a5ffecfce93 Revert "Introduce bug" d7a1ee454168081227ee82c5f11bb08f77e2643d Unrelated change e43a6fd3e94888d76779ad79fb568ed180e5fcdf Introduce bugWhen you revert the commit, you get dropped into an editor in case you want to can edit the commit message..
As an alternative to merging, you can use git rebase.
Instead of making a new commit which joins two branches together, it copies your changes on top of the other branch...
You use it just like a merge.
$ git rebase origin/masterDraw what this does, contrast it with a merge
One point of view on this is that your repository's commit history is a record of what actually happened. It's a historical document, valuable in its own right, and shouldn't be tampered with. From this angle, changing the commit history is almost blasphemous; you're lying about what actually transpired. So what if there was a messy series of merge commits? That's how it happened, and the repository should preserve that for posterity.
History is an audit trailThe opposing point of view is that the commit history is the story of how your project was made. You wouldn't publish the first draft of a book, and the manual for how to maintain your software deserves careful editing. This is the camp that uses tools like rebase and filter-branch to tell the story in the way that's best for future readers.
History is a story You should not change commits that exist outside your repository$ git rebase --interactive origin/master
pick a3895fe Try a bit harder to return a useful value from all methods pick 180e665 Release engineering (0.8) pick 2a6d832 Update Build.PL for newer build tools pick e1fac16 Use semantic versioning (http://semver.org/) # Rebase aa8a695..e1fac16 onto aa8a695 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. #
In it's simplest form, it allows you to throw away changes you have made locally.
$ git status # On branch master # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: index.html
$ git reset --hard HEAD is now at 5cc4a26 wr235825: small changes $ git status # On branch master nothing to commit (working directory clean)
# You can also change to a different commit before doing # the reset $ git reset --hard HEAD^^ HEAD is now at a51452d rewrite the 'begin' function
# This changes your commit, but leaves your files and index untouched # - changes that you have 'added' are still ready to commit # - changes to files are still ready to add $ git reset --soft HEAD^^
# This changes your commit and the index, but leaves your files untouched # - changes to files are now ready to add, even if you had already added them $ git reset --mixed HEAD^^
You can hide your changes away for later use
$ git status # On branch master # # modified: myscript.pl $ git stash Saved working directory and index state WIP on master: 5cc4a26 added a file
$ git status # On branch master nothing to commit (working directory clean)
Later on, you can restore your changes
$ git status # On branch master nothing to commit (working directory clean) $ git stash pop # On branch master # # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: myscript.pl no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (1a69a03f554a61ea50b3a9e2eb1182608cb50c8e)You can manage multiple stashes if you want....but that gets confusing :(
The official Git site-> http://git-scm.com/about
Data Assurance-> http://git-scm.com/about/info-assurance
Cheat sheet-> http://web.archive.org/web/20090419122050/swxruby.org/git-cheat-sheet.pdf
Github-> http://github.com
We have just discovered that there is a program in production that is not executable - the site is down!
But we can't deploy the latest code because it's not ready...
Please find the release tag 'release-1-0' in the repository 'https://github.com/catalyst-training/example1.git', and fix the bug!
Note that these problems are NOT a test - you haven't been given enough information yet It is just some everyday problems that we can work through together...# Clone the repository $ cd /tmp $ git clone https://github.com/catalyst-training/example1.git $ cd /tmp/example1 # Make a new branch (at the point we want to change) $ git checkout -b release-1 release-1-0 # Fix the problem $ chmod +x program $ git commit -a -m 'make the program executable' $ # git push origin release-1Discuss The fact that we can't push to that URL And even if we could, we can't push to the master branch * What would we normally do next (cherry-pick / merge), probably not rebase
I have just noticed that https://github.com/catalyst-training/example2.git has a dodgey commit. The commit message is 'Oooh, this smells a bit funny...'.
Please make it go away
Once again, you won't be able to push your changes - but that's ok.# Clone the repository $ cd /tmp $ git clone https://github.com/catalyst-training/example2.git $ cd /tmp/example2
# Work out what we want to do $ git log --oneline 5c90449 Remove the README file 3bec061 Some more stuff 3114205 Oooh, this smells a bit funny... 510043a Another list - snakes 6a624b3 Add a list of lists
# Rewrite the past $ git rebase --interactive 510043a
Wow, now that I see what you can do to tidy up the past, I realise I want more
Please add your favourite movie to the list of movies - but make it look like it was added with all the other movies.
Once again, you won't be able to push your changes - but that's ok.$ git log --oneline
# This will be more complex, from within this command you'll need to: # $ echo '* Highlander II' >> lists/movies.txt # $ git add lists/movies.txt # $ git commit --amend # $ git rebase --continue $ git rebase --interactive b9fb9c8
We have been working as two teams for a while now, and we have realised that the two teams are so interlinked that we should be all working from the same repository,
Please join https://github.com/catalyst-training/example1.git and https://github.com/catalyst-training/example2.git together!
Of course, I would like to keep all files and history from both teams intact.
This is a true story$ cd /tmp/example2 # Add another remote! $ git remote add ex1 https://github.com/catalyst-training/example1.git # Fetch the commits from example1 into this repo $ git fetch ex1 # Merge ex1/master into origin/master $ git merge ex1/master $ # git push origin masterDiscuss If there were conflicts, we could resolve them, or put the projects into subdirs first We could have used the directory /tmp/example1/.git as the URL for ex1 - that would have allowed us to include our bugfix into the merge * We still could!, and we could then cherry-pick our bugfix in!
Please create a github account, then 'fork' the repository 'https://github.com/catalyst-training/example3'
Then make a change, and create a pull request so I can accept your work.
You can do the whole thing in your browser!
This will take a bit more time, help with - Github account - SSH keys - fork vs clone - editing inside the browser - pull requests - etc...That was great, but now lets try again, using git-gui to make and push your commit.
I'll get you started...
$ cd /tmp $ git clone https://github.com/YOURNAME/example3.git $ cd /tmp/example3 $ echo 'Sean Connery' > $YOURNAME.txt $ git gui
Using your github account, push a 'password' to github.
$ echo 'butter' > password.txt $ git add password.txt $ git commit -m "I may regret this...." $ git push origin master
Now make it go away.
There are several ways to do this, but is it really gone?
# Amend the commit and force push $ echo '' > password.txt $ git commit -am "Nothing to see here..." $ git push -f origin master
# Rewind and force push $ git reset --hard HEAD^ $ git push -f origin master
# Delete the remote branch and push a new one $ git push origin :master $ git reset --hard HEAD^ $ git push origin master
Go to your 'example2' project, and try to merge in the 'kittens' branch...
# Create a conflict $ cd /tmp/example2 $ git merge origin/kittens $ git status
Oh, no, there are conflicts! Get me out of here!
or a rebase ...# Too hard, I'll try again some other time $ git merge --abort
Go to your 'example2' project, and try to merge in the 'kittens' branch...
# Create a conflict $ cd /tmp/example2 $ git merge origin/kittens $ git status
There are conflicts, but I think you can handle them!
# Let's give it a go... $ git mergetoolAlso, we should try this again after installing 'meld'
What if you make some changes, and you want to get back to safety?
$ cd /tmp/example2 $ echo 'A dragon will fix it' > lists/links.txt $ rm lists/movies.txt $ git status
$ git reset --hardCould we use git-reset? git-checkout? gitk? git-clean?
open source technologists