- Make sure you have configured git tools: see configuring tools.
- Make sure you have updated git aliases: see how to register aliases.
After each commit in our branch, in order to be up-to-date with the integration branch.
WHY? Because you integrate the progress of the base branch into YOUR branch, so that you will have fewer conflicts to resolve, and the rebase process will take less commits to apply when rebasing against your branch.
The sooner you REBASE your branch, the fewer conflicts to resolve.
First of all, DO NOT allow a day without performing at least ONE REBASE against
your master
or integration branch.
Try to have only one integration branch. This way we will keep an eye only
in one branch, e.g. master
or your integration branch.
AVOID as much as possible perform a rebase on stable branches, such as
master
. Remember that the git rebase
command rewrites the branch’s history.
That is becasuse when performing a rebase
or rebase -i
, you can reorder,
edit, merge, or delete commits, then the identity of each commit (SHA) can change.
For instance, let's say the last commit on master
is as follow:
6ace7bf4 [US-703] Fixed bug #12 on MultiSelect component
and then we perform an interactive rebase to merge old commits:
git rebase -i HEAD~4
. What happens next is that the history on master
has changed, and the last commit has a new SHA, something like this:
c18b70b2 [US-703] Fixed bug #12 on MultiSelect component
.
After reordering or merging old commits, the identity of the subsequent commits
are changed, giving them a new SHA. That means that my local branch master
has
diverged from the remote branch master
. So to upload my changes to the server
we have to force the push: git push --force-with-lease origin master
. But take
into account that usually the master
branch is protected against a forced-push,
and also, performing forced-push on stable branches is NOT A GOOD PRACTICE.
The problem comes when somebody is working on a branch that has master
as the
base branch, with the last commit:
6ace7bf4 [US-703] Fixed bug #12 on MultiSelect component
, and now when he
performs a rebase against master
, turns out that the master
branch is
different from the previous one which now has the last commit:
c18b70b2 [US-703] Fixed bug #12 on MultiSelect component
. Apparently they
are the same, but the rebase action will prompt a lot of conflicts because
you are duplicating commits, that is, rebase
will keep the commits of the
old master
, and then it will apply the commits of the new master
in your
branch.
For these types of scenarios, git
comes with the powerful rebase --onto
.
Using git rebase --onto
you can rebase a part of a branch using the base
as some other branch (or SHA). The syntax for Git command is:
git rebase [--onto <new-base>] [<old-base> [<branch>]]
In our case, after the master
branch was altered, we can run the rebase
by specifying the new master and the old-base commit in our branch.
The commands will be like below:
# let's say my working branch is feat-x
# get the last state of remote master
git fetch origin master
# print the log to find the old-base SHA commit
git log --oneline
git rebase --onto origin/master 6ace7bf4 feat-x
We are telling to rebase --onto
that apply the new-base commits on
origin/master
over the old-base SHA in our branch feat-x
.
Read more about rebase --onto
:
All git alias
used in following snippets use the integration branch named integration
.
You may want to use another integration branch or perhaps you integrate changes
against master
directly.
So given another integration branch, e.g. team-integration
, you should change -integration
to -team-integration
(or -master
) in the following sources:
git-alias.sh and git-alias.ini.
The last section ALL ALIAS contains the list of all alias registered here, and also the original git commands that are aliasing.
You can register aliases in three ways:
- Download git-alias.sh and open a
bash
/shell
terminal where the file is, and run:sudo chmod +x git-alias.sh
andbash git-alias.sh
- Open any terminal you like and run
git config --global -e
, the default editor will be opened and then, paste the content of git-alias.ini at the end of file, or at the bottom of the[alias]
placeholder. - Download git-alias.sh and git-alias.js,
then open any terminal in the folder where the downloaded files are, and run
sudo chmod +x git-alias.sh
andnode "git-alias.js"
. Tip: you can create a npm script to register git aliases in your project, e.g."reg:alias": "node scripts/git-alias.js"
When you want to create a new user-story branch, let's say us-753
, the ideal way is starting
from the integration branch, as this will be updated with the latest changes.
# first, update the integration branch,
# then, create us-753 from the integration branch
# and later, perform a checkout to us-753
$ git checkout integration
$ git newbr us-753
Why use lowercase when naming branches?
Because it reduces errors in the file system when dealing with different platforms.
Linux is case-sensitive for file names, while Windows is not.
When you create a branch, git internally creates a folder for that branch.
What if you create the branch us-753
, and another user also creates the branch US-753
?
Well, as git runs under a subset of unix-core, it can have both branches with both
folders respectively in the file system, us-753
and US-753
, but someone in a Windows
machine will have troubles trying to get us-753
or US-753
, because the file system
is not case-sensitive.
After some progress, you may want to save your changes.
You can create a new commit message, or add your changes to the last commit saved (amend).
# Commit all: creates a new commit message including all changes
$ git commitall "[US-753] Fix issue #16 with null dataset of Linear component"
# Or commit amend: append all changes to the last commit (no message added)
$ git amend
# Push with --force-with-lease flag to remote branch (us-753)
$ git pushf
Once you are uploaded your changes and the workspace is clean, it is good time to check if the integration branch contains new commits to update our personal branch with those commits.
# Get the latest changes from integration branch and include them into us-753
$ git rebasebr
# Also you can rebase against another branch:
# git rebasebr master
# Fix conflicts if exist
# git mergetool
# git rebase --continue
# Upload changes to remote us-753
$ git pushf
Let's say these are our last commits (git log --oneline -4
,
top shows recent commits, bottom shows older commits)
{1} c8fc9bb0 [US-753] Fix issue #16 with null dataset on Linear component
{2} 6ace7774 753 WIP 2
{3} af4a49fa 753 WIP
{4} c18b70b2 [US-703] Fixed bug #12 on MultiSelect component
This record is ugly, and if you are working with pull request, your PR could be rejected, or the approver could squash your WIP commits.
The commit {1} contains the end of user-story-753, but commits {2} and {3} are Work-In-Progress of the same story, so it would be nice to merge them into one commit.
Here is where git rebase -i
can help us to organize our commits before
merging them into another branch.
The alias git rebaseii
allows you to edit the last X commits,
this way you can modify messages, remove, reorder or meld
commits in your working branch.
# Rebase us-753 and open editor with last 4 commits
$ git rebaseii 4
# Upload changes to remote us-753
$ git pushf
See the rebase interactive screen to edit the last 4 commits.
First, you rebased your branch against
the integration branch with git rebasebr
, later
you reorganized your commits with git rebaseii
, and now you are ready to
merge the branch us-753
into the integration branch.
# 1. Checkout to the integration branch and update it
# 2. Merges your feature branch (us-753) into the integration branch
# 3. Push the integration branch to the server (origin)
# 4. Checkout again to your initial branch
$ git mergeto integration
Alternatively, if you have finished the feature, and no more work is needed in the branch, you may delete the feature branch after being merged into the integration branch.
# 1. Checkout to the integration branch and update it
# 2. Merges your feature branch (us-753) into the integration branch
# 3. Push the integration branch to the server (origin)
# 4. Delete the feature branch from both, local and server
$ git finishto integration
Tip: if you have more than 3 commits on your branch, and you still working on a feature, consider rebase your own branch with
git rebaseii
or integrate some commits to the integration branch, because the more commits you have in your branch, the more times probably you will have to resolve conflicts.
For instance, let's suppose your branch is ahead integration branch by 5 commits,
and other developer pushes 2 commits to the integration branch where a file
you have edited also was modified. You decided to perform a git rebasebr
,
and it turns out that the first old commit in your branch has the file in conflict;
now you have to resolve the conflict 5 times across your 5 commits. Just imagine
if your branch is ahead by 10 commits or more...
# Creates a new branch from the current branch.
# Alias:
# git pull origin <current-branch> --rebase && git checkout -b <branch-name>
$ git newbr branch-name
# Removes a branch from local and remote.
# Alias:
# git branch -D <branch-name>; git push origin --delete <branch-name>
$ git rmbr branch-name
# Gets the name of your current working branch.
# Alias:
# git branch | grep \* | cut -d " " -f2
$ git currentbr
# Update references from remote branches and prints the list of remote branches.
# (HEAD, master and the default integration branch are not listed)
# Alias:
# git remote update origin --prune && git branch --remote
$ git syncbr
# Adds all untracked / unstaged files and creates a new commit with message.
# Alias:
# git add -A && git commit -a -m "message"
$ git commitall "[US-741] Message"
# Append all untracked / unstaged files to the last commit. No message is added.
# The SHA of the last commit is altered, and the modified files are appended to it.
# Alias:
# git add -A && git commit -a --amend --no-edit
$ git amend
# Pushes the current working branch to the server with --force-with-lease flag,
# useful when the "git amend" alias is used.
# Alias:
# git push --force-with-lease origin <current-branch>
$ git pushf
# Performs an interactive rebase on your local branch.
# Alias:
# git rebase -i HEAD~<max-commits>
$ git rebaseii 5
# Performs a rebase against the default integration branch,
# in order to be updated with latest commits of the base branch.
# Alias:
# git fetch origin integration && git rebase origin/integration
$ git rebasebr
# Performs a rebase against another branch (master)
# Alias:
# git fetch origin master && git rebase origin/master
$ git rebasebr master
# Peforms a rebase against another branch changing the old-base
# as some other branch or SHA.
# Alias:
# git fetch origin <branch> && git rebase --onto origin/<branch> <old-base-SHA>
$ git rebaseonto master 6ace7bf4
$ git rebaseonto integration 6ace7bf4
# Performs a rebase of the current branch against the remote branch.
# Alias:
# git pull origin <current-branch> --rebase
$ git pullbr
# Reset your local integration branch to the server's integration branch.
# Alias:
# git checkout integration && git fetch origin integration && git reset --hard origin/integration
$ git resetbr
# Reset another local branch to the state of that branch in the server.
# Alias:
# git checkout <branch> && git fetch origin <branch> && git reset --hard origin/<branch>
$ git resetbr master
# Merges your current working branch into the destination branch, but first updates
# the destination branch. If no parameter is provided, the destination branch is
# the default integration branch. After the merge, the destination branch will be
# pushed to the server and then you'll be back to the initial branch.
# Alias:
# git resetbr integration && git merge <initial-branch> && git push origin integration && git checkout <initial-branch>
$ git mergeto
# Merges your current working branch into the target branch, but first updates
# the target branch. After the merge, the target branch will be
# pushed to the server and then you'll be back to the initial branch.
# Alias:
# git resetbr <target> && git merge <initial-branch> && git push origin <target> && git checkout <initial-branch>
$ git mergeto master
$ git mergeto develop
# 1. Checkout to the integration branch and update it
# 2. Merges your current branch into the integration branch
# 3. Push the integration branch to the server (origin)
# 4. Delete the feature branch from both, local and server
$ git finishto
# 1. Checkout to the target branch and update it
# 2. Merges your current branch into the target branch
# 3. Push the target branch to the server (origin)
# 4. Delete the feature branch from both, local and server
$ git finishto master
# Merges a specific branch into the default integration branch.
# After that, the integration branch is pushed to the server.
# (the "mergeto" alias makes sure the target branch is updated first)
# Alias:
# git checkout integration && git merge us-753 && git push origin integration
$ git mergebr us-753
# Merges the branch "us-753" into the "master" branch.
# After that, the "master" branch is pushed to the server.
# Alias:
# git checkout master && git merge us-753 && git push origin master
$ git mergebr us-753 master
# Checkout to a <branch> and deletes all branches already merged into that branch.
# This method is destructive. Do not use it unless you know what you are doing!
# Alias:
# git checkout master && git branch --remote --merged master | xargs git rmbr
$ git cleanmerged master
# Temporarily untrack changes in a file, so it won't be added in commits.
# Alias:
# git update-index --assume-unchanged <path>
$ git untrack path/to/file
# Tracks changes again from an untracked file, so it can be added in a commit.
# Alias:
# git update-index --no-assume-unchanged <path>
$ git track path/to/file
# Show commits in the current branch by an specific author, starting at some date.
# Alias:
# git log --pretty="custom-format" --date=short --author=david.rivera --since=2018-10-01
$ git logauthor david.rivera 2018-10-01
# Show commits in the current branch by an specific author, between a date range.
# Alias:
# git log --pretty="custom-format" --date=short --author=david.rivera --since=2018-10-01 --until=2018-11-01
$ git logauthor david.rivera 2018-10-01 --until=2018-11-01
# Show commits in all local branches by an author, starting at some date.
# Alias:
# git log --pretty="custom-format" --date=short --author=david.rivera --since=2018-10-01 --all
$ git logauthor david.rivera 2018-10-01 --all
# Show commits in the current branch by an author, for a specific file, starting at some date.
# Alias:
# git log --pretty="custom-format" --date=short --author=david.rivera --since=2018-10-01 --follow -- path/to/file
$ git logauthor david.rivera 2018-12-01 --follow -- path/to/file
Learn more about pretty-format
at https://git-scm.com/docs/pretty-formats.