This will allow you to improve your git workflow by creating shortcuts to more complicated git commands.
To see all the current alias confgurations of your git instance, run
git config --list | grep alias
One way to add git aliases is through direct command and it looks something like:
git config --global alias.<your_alias_command> <the_git_command>
For example, the command
git checkout can be replaced with an alias such as
git co (less typing). To achive that, run:
git config --global alias.co checkout
Another way to achive the same effect (for *nix) users is to locate the
~/.gitconfig file and below the
[alias] tag add:
: [alias] co = checkout :
Here are some nice aliases to view the commit history:
[alias] lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
Simple mistakes can be made when committing (eg: wrong spelling in the commit message or a forgotten file). Rather than committing again the forgotten file with a proper message, one can add the file and modify the commit message using:
git commit --amend -m "New Message"
The commit history will show only one commit containing the previous contents and the amended contents.
NOTE!!! this should be done only for the local commits. DO NOT amend public commits.
Now imagine a situation whereby you are frequently committing code (eg: write some debugging code and then test the results several times), only to find yourself at the end of the process to have 10 commits for only a few changes in the real code.
This would look rather ugly if you were to push to the public repository, after all, the real changes to the code a only a few and the intermediary helping steps in between are not relevant.
In those cases, we could
squash the last 10 local commits into a single commit, like so:
git reset --soft HEAD~5
Since you are using git, most probably it is for collaborative purposes, so, the local work needs to be shared using a remote repository. To see the settings towards the remote repo, run
git remote -v and the result will be something like:
origin https://github.com/Microsoft/MS-DOS.git (fetch) origin https://github.com/Microsoft/MS-DOS.git (push)
Here you can see that the default name of the remote is origin, and two remote addresses (one for fetch and one for push).
For detailed information about the remote repo, run
git remote show <remote_repo_name>:
git remote show origin
To add our own remote:
git remote add our-own-remote https://example.com
To remove the remote configuration:
git remote remove <remote_repo_name>.
This will result in:
origin https://github.com/Microsoft/MS-DOS.git (fetch) origin https://github.com/Microsoft/MS-DOS.git (push) our-own-remote https://example.com (fetch) our-own-remote https://example.com (push)
In order to download information from the remote repo, but not merge it with our local repo, all we have to do is
git fetch <remote_repo_name>:
git fetch origin
To merge the changes of the remote repo into our own local repo, we run
git merge <remote_repo_name>/<remote_branch> <local_branch>:
git merge origin/master master
To accomplish the above two steps in one command, we use
git pull, which is an alias of
git fetch && git merge
This feature is very useful for collaboration in case the collaborators do not have write access to the repository.
As a first step, create a fork of the repository you want to contribute to, then clone that fork onto your own local machine.
In order to keep track of the changes made on the original repository, you need to add an upstream remote address to your configuration.
git remote add upstream <original_repo_git> git remote -v show
You will see two remote repositories, origin which is our fork and upstream which is the original repository. The name upstream can be anything easy to remember.
origin https://www.our-fork.com (fetch) origin https://www.our-fork.com (push) upstream https://wwww.original-remote-repository.com (fetch) upstream https://wwww.original-remote-repository.com (push)
To incorporate changes from upstream into our own fork:
git fetch upstream git merge upstream/master git push origin master
We use tagging in git to mark important milestones in the development process, like a stable release or a feature release, etc...
Listing all tags in the repo in alphabetical order:
To search for tags based on regex, one can run:
git tag --list <pattern>
To create a tag which contains extra information:
git tag -a "<tag_name>" -m "Some message about the tag"
In order to push the tags to the remote origin repo:
git push origin 1.1 // for one tag called 1.1 git push origin --tags // for all tags
Retrieving extra information about a tag can be achieved through:
git show <tag_name>
Simply put, branches are references to a certain commit point in the repository. In a sightly more fancy way, branches are directed acyclic graphs or DAG. By default, we have a master branch (the name can be anything, but most of the time no one bothers to change it). We can diverge from this branch and create extra branches based on our needs.
Let's say we want to create a development branch and switch the HEAD to it:
git checkout -b develoment
If the goal is only to create the branch but not switch, we can run only:
git branch development
Like this, the original branch remains unchanged while we are working on our divergent branch.
To see how the branching looks like, use the log command:
git log --oneline --decorate --graph --all
To list up the branches along with their last commit:
git branch -v
Having this great feature of branching, is what makes git very popular. However, this would be useless if we cannot combine (merge) the code that we branched out from, with the master track or the branch that we diverged from.
This is where merging and rebasing comes into play.
Imagine you start working on a feature and you branch out from the master at some point:
git checkout -b feature
But there is a bug in the master branch and we need to fix it before we are done with the feature branch. So we switch (
checkout) from our current working branch to the master branch, and fix the problem.(NOTE you should commit whatever work is untracked in the feature branch, before switching).
git checkout master git checkout -b fix
So now we are done with the fix branch and we will merge it into the master before continuing the work on the feature branch. You could also delete the fix branch once it is merged.
git commit -m "done with the fix" git checkout master git merge fix git branch -d fix # delete the branch git checkout feature
We can make some more commits on our feature branch and then merge it into the master branch.
git checkout master git merge feature
Rebasing is also a type of merging, but it works by applying the patch of changes introduced by the branch we are working on and applying it on top of the target branch (eg: the master branch).
To achieve that:
git checkout feature git rebase master
As a rule of thumb, do NOT rebase on public branches, use regular merge because is safer. Merging is a safe option that preserves the entire history of your repository, while rebasing creates a linear history by moving your feature branch onto the tip of master.
If you’re not entirely comfortable with git rebase, you can always perform the rebase in a temporary branch:
git checkout feature git checkout -b temporary-branch git rebase -i master # [Clean up the history] git checkout master git merge temporary-branch
7.3. Merging conflicts
Sometimes, things are not that smooth, especially when the same part of the code was modified in two different branches. In those cases, we get a merge conflict, and that needs to be solved manually.
Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.
The the part(s) of file(s) with conflicts will be amended with something like:
<<<<<<< HEAD:home.py print("Hello World!") ======= print("Great to see you, World!") >>>>>>> feature:home.py
The part between
<<<<<<< HEAD and
======= is the code version in your target branch, while the part between
>>>>>>> feature is the code version in our feature branch.
These files are not staged. Once you fix all the conflicts, do a
git add ., which will signal that the conflicts are solved, followed by a