Introduction
Git is a distributed version control system used to manage the source code of Digger. We can use apt-get to install Git:
$ sudo apt-get install git
Before using Git, let’s do a few initial configurations:
-
The initial branch name to use in all new repositories:
$ git config --global init.DefaultBranch main -
Identify the author of the commits:
$ git config --global user.email "you@example.com" $ git config --global user.name "Your Name"
Configuring Git to Simplify Authentication
For the moment, every time we push code to GitHub the prompt asks for a username and password. We can bypass this step by registering a SSH key. To do that, we first check whether there is already an existing SSH key we can reuse:
$ ls -al ~/.ssh
If files with the extension .pub are listed then one of them can be reused to authenticate to GitHub. If not, then we can create one:
$ ssh-keygen -t rsa -b 4096 -C "[firstname.lastname]@domain.com"
Enter file in which to save the key (/Users/[user]/.ssh/id_rsa): [Press enter]
Enter passphrase (empty for no passphrase): [Type a passphrase]
Enter same passphrase again: [Type passphrase again]
The generated keys need to be protected with the right permissions otherwise the access won’t work:
$ chmod 700 ~/.ssh
$ chmod 644 ~/.ssh/id_rsa.pub
$ chmod 600 ~/.ssh/id_rsa
The next step is to add the new key - or an existing one - to the ssh-agent. This program runs the duration of a local login session, stores unencrypted keys in memory, and communicates with SSH clients using a Unix domain socket. Everyone who is able to connect to this socket also has access to the ssh-agent. First, we have to enable the ssh-agent:
$ eval "$(ssh-agent -s)"
And add key to it:
$ ssh-add ~/.ssh/id_rsa
The next step is to make GitHub aware of the key. For that, we have to copy the exact content of the file id_rsa.pub and paste into GitHub. To make no mistake about the copy, install a program called xclip:
$ sudo apt-get install xclip
And then copy the content of the file id_rsa.pub in the clipboard:
$ xclip -sel clip < ~/.ssh/id_rsa.pub
The command above is the equivalent of opening the file ~/.ssh/id_rsa.pub, selecting the whole content and pressing Ctrl+C. This way, you can paste the content on GitHub when required in the next steps. On the GitHub side:
-
Login at https://github.com
-
In the top right corner of the page, click on the profile photo and select Settings
-
In the user settings sidebar, click SSH keys
-
Then click Add SSH key
-
In the form, define a friendly title for the new key and paste the key in the Key field
-
Click Add Key to finish with GitHub
To make sure everything is working, lets test the connection:
$ ssh -T git@github.com
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes
_
Hi [username]! You've successfully authenticated, but GitHub does not
provide shell access.
Repository
We can test the installation by cloning the Digger repository:
$ mkdir -p ~/java/projects/digger
$ cd ~/java/projects/digger
$ git clone git@github.com:htmfilho/digger.git .
This configuration works only when we use a ssh connection to GitHub. To verify that, go to one of your local GitHub projects and check the url pointing to the server:
$ cd ~/java/projects/digger
$ git remote -v
If the url starts with https:// then you are using https instead of ssh. In this case, you should change the url to the ssh one:
$ git remote set-url origin git@github.com:htmfilho/digger.git
The automatic authentication should work after that.
By the way, when the repository is too big, with a long versioning history, we
may consider cloning only the latest version to kick-off our day faster. We do
it using the --depth parameter:
$ git clone --depth 1 -b master git@github.com:htmfilho/digger.git
When we have our configuration done and everything is working, before stopping for the day, run:
$ git fetch --unshallow
to download the rest of the repo. We want this to enable the “blame” feature in our IDE.
Changing The Author To The One Recognizable by GitHub
In case your default Git author is not the same as GitHub, configure the author of the repository:
$ git config --local user.name "John Doe"
$ git config --local user.email "john@doe.org"
It can also be done to a specific commit:
$ git commit --author#"John Doe <john@doe.org>"
Setting Pull Behaviour
The git pull command merges the remote branch into the local branch with a merge commit, but we don’t think this commit is useful. We want to make sure our commits represent changes made by developers only. So, we would like to ask you to use rebase to merge remote branches locally. You can do it at every pull with:
$ git pull --rebase origin master
or change a local configuration to make it the default pull behavior:
$ git config --local pull.rebase true
Note: you don’t need to run this local configuration if you already have it globally.
Pruning Deleted Remote Branches
When branches are removed from origin, this change is not automatically reflected in the local clone when doing a git pull or git fetch. To have deleted remote branches automatically pruned from the local repo, set the following config:
$ git config --global fetch.prune true
Workflow
Let’s first clarify that, when discussing the workflow, we use the term “Issue” to refer to a change request, not a technical issue that could happen when we are dealing with version control.
Before
Before doing the work.
During
While doing the work.
Dealing with Unexpected Issues
Imagine a scenario where you are busy, working on an issue, then a more urgent issue comes up, so you have to pause what you are doing to shift context. It happens all the time. Git is our best friend in this kind of situation, but we have to use it properly.
The dev Branch Must Be Immutable
I was working on an issue and, in the spirit of saving time, I have commited changes directly to the branch main. In this project, we could not push changes to the remote branch main, so we needed to create a separate branch from main and create a pull request. I thought I could simply do that and move on, which normally works when everything goes well.
However, I may need to test something in main before the pull request is accepted, but since it contains changes then it doesn´t behave the same way. I would need to reset my local dev branch to be able to reproduce the issue. It can be done running:
$ git reset --hard origin/main
Change the Most Recent Commit Message
The command below will open the text editor where we can change the commit message:
$ git commit --amend
Adding a File to the Most Recent Commit
$ git add missed-file.txt
$ git commit --amend
Undo the Most Recent Commit
$ git reset HEAD~
After
Once the work is done, many situations can happen. Here is how to deal with them.
Changing Several Commits in Bulk
If commits were done with a wrong author, use Git Rebase to fix the authors of the commits:
$ git rebase -i -p <commit-id>
$ git commit --amend --author="John Doe <john@doe.org>"
$ git rebase --continue
$ git push -f origin master
The rebase starts from the commit after the informed <commit-id>. It wouldn’t work if the rebase needs to consider the very first commit. To include the first commit, start an interactive rebase of all commits using git rebase -i --root.
Undo One or More Commits Pushed to Remote
Update the working branch to have it as a backup:
$ cd ~/java/projects/project
$ git pull origin master
Create a new clone to use as experimentation:
$ cd ..
$ git clone git@github.com:htmfilho/project.git project-temp
$ cd project-temp
You can also clone a specific branch:
$ git clone --branch bugfix git@github.com:htmfilho/project.git project-temp
Look at the log to see the id of the latest valid commit:
$ git log
Force the head of the tree to point to the latest valid commit:
$ git reset –hard 73d48037
Force the new head into the remote branch (origin):
$ git push –force origin master
The clients that still have the old commits should update their local branches accordingly before the next push:
$ git reset –hard origin/master
Remove a File From the Repository Without Deleting It
For a single file:
$ git rm --cached mylogfile.log
For a single directory:
$ git rm --cached -r logs
Restore a Deleted Branch
The follow commands recover a branch that was deleted locally with the command git branch -D issue-52. Use reflog to figure out the
$ git reflog
Take note of the
$ git checkout -b issue-52 dc4b3ff
Look at the log to see if it contains what you are looking for:
$ git log
Finally, move to the master branch and merge the recovered branch into it:
$ git checkout master
$ git merge issue-52
Cherry Pick a Commit from One Branch to Another
In some projects, we may have release branches. The ones that are deteched from the main branch when all the features planned for the release are done. This release branch allows development work to continue in the main branch without affecting what is about to be released.
However, when the release branch is deployed to a staging environment, we may find some bug that needs to be fixed before deploying to production. In this scenario, we have to push the same fix to the release branch as well as to the main branch, to ensure the fix is carried on to future releases.
To save some effort, start working on the release branch, commiting the fix and creating a pull request. Then follow these steps to add the fix to the main branch:
-
Get the hash or the hard range of all commits related to the fix. You can see the hashes using:
$ git log -
Move to the main branch:
$ git checkout main $ git pull -
Create a branch for the change:
$ git checkout -b fix-123 -
Cherry-pick the fix from the release branch using the hashes:
$ git cherry-pick <hash1> <hash2> <hash3>
After this, you may have to fix code conflicts, making sure the fix is adapted to recent changes in the main branch.