Alright guys. So this is a tutoral to learn git. While I have learnt it many times, I want to this time also document it properly, so that I don't end up forgetting it once more.
Now I don't use it often so it's OK if I do forget some of the syntax. But the idea here is to have a self created ready reference of the things I should know and why I should do it. Because the thing with git is that while learning the basic commands is easy, I end up forgetting the underlying philosphy or reason to do something. That then just doesn't work with me.
Alright, enough said. Let's get started.
OK so the way I plan to learn it is to use Git Immersion site. And probably this document itself can be used to just learn the basics of the git syncing.
- We should set up the global name and email the first time we start git. This enables to use this setting throughout the git usage.
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
- Additionally to take care of the cross-platform development, we use the following :
git config --global core.autocrlf input # This makes sure to convert CRLF to LF while commiting but not the other way around.
git config --global core.safecrlf true # This is used to ensure that the binary files' eol is not touched accidentally.
- Create a folder learngit and add this file
learngit.md
in it. Additionally create a newhello.py
program.
mkdir learngit
cd learngit
touch learngit.md hello.py
- To create a repository out of a directory run the following command:
git init
- This basically adds the file to the 'staging area' and starts 'monitoring it' but doesn't commit it. This allows to commit separate files at different times.
We can also use git add .
to add all the files in current directory to the staging area. But we should ensure to run git status
first to ensure nothing gets unintentionally.
git add hello.py
git status
- We commit the changes using commit command . We should provide -m flag to provide meaningful message along with commit. If -m flag is forgotten then it will automatically open an editor for you to add the commit message.
git commit -m "my meaninful commit message"
- When
git add filename
is executed it basically tell git to note the current state of the file to be commited later. Any more changes done in thegit add
will not be captured in the current commit.
- We can see what all changes have been made in the repository using:
git log
- One line display of changes made:
git log --pretty=oneline
- Various options:
git log --pretty=oneline --max-count=2
git log --pretty=oneline --since='5 minutes ago'
git log --pretty=oneline --until='5 minutes ago'
git log --pretty=oneline --author=<my name>
git log --pretty=oneline --all
git log --pretty=format:'%h %cd %s (%an)' --since='7 days ago'
git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
Talking about last option which can be preferred way: --pretty="..." defines the format of the output. %h is the abbreviated hash of the commit %d are any decorations on that commit (e.g. branch heads or tags) %ad is the author date %s is the comment %an is the author name --graph informs git to display the commit tree in an ASCII graph layout --date=short keeps the date format nice and short
We can set up aliases for some commonly used and long git commands in .gitconfig
file in $HOME directory. Add the following in there:
[alias]
co = checkout
ci = commit
st = status
br = branch
hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
type = cat-file -t
dump = cat-file -p
Now we can simply write git ci
for commit and git st
to check status.
- We can go back to any older version (snapshot) of a file by using
checkout
command. Here the<hash>
is determined by the git log command (first 7 characters of hash are enough to uniquely determine.)
git checkout <hash>
cat hello.py
- To go back to the original version use below command.
master
is the default branch, but we can create multiple branches as required. By typing the name of the branch during checkout, we go to the latest versin of that branch.
git checkout master
cat hello.py
- We can call the current version of the file as v1.
git tag v1
- To tag the version immediately prior to current one, we need to first checkout the version 1 step prior to v1 by writing v1~1
git checkout v1~1
cat hello.py # Verify that we are at the correct version
git tag v1-beta
- Now we can go back and forth between the two tagged versions easily.
git checkout v1
git checkout v1-beta
- To view the currently available tags.
git tag
- Tags are also viewable in the logs.
git hist master --all
- To undo changes before staging the changes, we can simply checkout the last correct version (usually master).
git checkout master
- To undo the changes after staging we can use the
reset
command. Thereset
command resets the staging area to be whatever is in HEAD . This clears the staging area of the change we just staged. But we should still dogit checkout master
to get the last commited version and remove the unwanted changes from the file.
git add hello.py # Add modified file in staging area
git status
git reset HEAD hello.py # To remove file from staging area
git checkout master # To actually revert the changes back to last version.
- To undo the changes after commit we should ideally create a new commit to revert the unwated changes. Instead of HEAD (which we revert the most recent version) we can use the hash of any commit to revert to that particular version.
git add hello.py # Add modified file in staging area
git commit -m "Unwated changes commited"
git revert HEAD # This will open the editor to ask for revert comments.
git log # We can now see both the wanted commited as well as the revert commit.
- We can also undo the changes after commit
permanently
by using thereset
command. What this will do is that it will remove (hide) the incorrect commit from the log history.--hard
parameter ensures that the working directory also gets updated with the new branch head.
git tag oops # Tag the current commit as oops
git hist
git reset --hard v1 # v1 is the tag of the version which we want to commit but we can also provide the hash value instead.
git hist # Now the unwanted commits will not be visible.
git hist --all ## This parameter will show the unwanted commits also.
git tag -d oops # This removes an existing tag.
git hist --all # Now the unwated commits are not seen anymore because of removed tag
However, care should be taken to use the reset command on remote repositories as it can cause confusion with the other users. Using reset we can modify the branch pointer to point to anywhere in the commit tree.
- To commit an additional change with the latest commit we can do the following.
git add hello.py # Partial change
git commit -m "Add an author name"
git add hello.py # After making addtional change here.
git commit --amend -m "Add author name and email"
git hist # Review the changes
-
Two ways to move the files and inform git about it.
- Directly move the file through git. Advantage is that git immediately knows about the move and keeps the changes in the staging area.
mk dir lib git mv hello.py lib git status
- Fir move the file through OS commands and later inform git about it.
mk dir lib mv hello.py lib git add lib/hello.py git rm hello.py # Removes file from staging area, and from working directory if available there. git status
ls .git # Shows the structure of folder where all git "stuff" is stored.
ls .git/objects # Shows a bunch of directories with names correspnonding to first 2 letters of hash of the objects stored in git
ls .git/objetcs/<dir> # Shows the file which contain the objects stored in git. These files are encoded but are used to identify the object version.
cat .git/config # This is a project specific config file and will override .gitconfig in $HOME
ls .git/refs # Shows the way to reference the commits
ls .git/refs/heads # Shows the ways to reference branches
ls .git/refs/tags # Shows all the tags created for commits
cat .git/refs/tags/v1 # Tag v1 simply points to the hash code of corresponding commit.
cat .git/HEAD # HEAD file contains the reference to the current branch. Usually points to master by default.
- We can make use of the
git type
andgit dump
aliases we defined earlier to traverse and see the content of the files and structure inside git object.
git cat-file -t <hash> # Shows the type of the commit object
git cat-file -p <hash> # Shows the corresponding dump of the commit object
git cat-file -p <treehash> # We can use the hash of the tree to further traverse inside (folder/ git structure)
git cat-file -p <blobhash> # Blobhash will probably show the content of the corresponding file
- A Branch can be defined to isolate any changes from the master (branch).
git branch <branchname>
git checkout <branchname>
git checkout -b <branchname> # Short cut for above 2 commands
- To Simply switch between branches we can use 'git checkout` :
git checkout master
- Use
git log
along with--graph
parameter. All use--all
flag to ensure all branches are shown in the log.
- If master branch got changed while working on another branch (say 'greet') then we can make use of
git merge
to get the master changes into the greet branch. But this cause ugle graphs in the log. So probablygit rebase
is better to use.
git checkout greet
git merge master
git hist --all
- To rebase the branch :
git checkout greet # greet is another branch name
git rebase master
git hist
- The advantage of rebase is that greet branch history gets 'overwritten' by master so that it becomes part of greet branchh. this leaves the chian of commits linear and much easier to read.
-
We shouldn't use rebase :
- If the branch is public and shared with others. Rewriting publicly shared branches tend to screw work of other members of the team.
- When the exact history of the commit branch is important to know.
-
So usually we should use Rebase for short-lived, local branches and merge for branches in the public repository.
- If the head of one branch is already a direct ancestor of another, the merge simply moves the head in the commit tree. This is called fast forwarding and there is no chance of conflict here.
git checkout master
git merge greet # To merge greet branch changes into master
git hist
- To do a local clone we use below command. This creates an exact copy of the original repo with all the files and folder. The commit history is also copied as is, only difference being the name of the repo will be different in the cloned repo.
git clone hello cloned_hello # Here hello is existing repo
- A repostiory on github exists is called a
remote
respository. We may create a local copy of the remote repository on our local computer by first determining the full url of the repository on github. This can be found by using the 'clone' button on the repository page. Then we use the following command:
git clone https://github.com/codeRSH/learngit.git # Full URL of your remote repo here.
- To check the name of the remote repository use below :
git remote
- Conventionally a Remote repository (Primary Central repo) is called as 'origin'. To get more information about origin:
git remote show origin
- Git has all the commits from the original repository, but branches in remore repository are not treated as local branches. To see the list of branches
git branch # To see list of local branches
git branch -a # To see list of all branches (local + remote )
git fetch
command will fetch new commits from the remote repository, but it will not merge these commits in the local branches. Once data is fetched we can then amnually merge the changes usinggit merge
.
git fetch
git merge origin/master
- We can combine the effect of
git fetch
andgit merge origin/master
using simplygit pull
git pull
- We can add a local branch to track a remote branch (other than remote/master).
git branch --track greet origin/greet
git brach -a
git hist # greet local branch will now show along with origin/greet
- Bare Repositories are without working directory. They are usually used for sharing.
- Conventionally repositories ending in '.git' are bare repositories. Essentially it is nothing but the .git directory of a non-bare repo.
cd ..
git clone --bare learngit learngit.git
ls learngit.git
- We can add the bare repository as a remote to any existing repository.
cd learngit
git remote add shared ../learngit.git
- To push the change to remote repository we use:
git push shared master
# Here shared is the remote repo name and master is the branch of local repo receiving the changes.
- Usually we have to provide both the remote repo and remote branch name while pushing the changes.
git remote add shared ../learngit.git # Add learngit repository as a remote repo.
git branch --track shared master
git pull shared master
- Some of the topics to research by own:
- Reverting Committed Changes
- Cross OS Line Endings
- Remote Servers
- Protocols
- SSH Setup
- Remote Branch Management
- Finding Buggy Commits (git bisect)
- Workflows
- Non-command line tools (gitx, gitk, magit)
- Working with GitHub