How to use Xcode

Issue #499

Build setting

Build Library For Distribution

It turns on all the features that are necessary to build your library in such a way that it can be distributed

What does this error actually mean? Well, when the Swift compiler goes to import a module, it looks for a file called the Compiled Module for that library. If it finds one of these files, it reads off the manifest of public APIs that you can call into, and lets you use them. Now, this Compiled Module Format is a binary format that basically contains internal compiler data structures.

And since they’re just internal data structures, they’re subject to change with every version of the Swift Compiler. So what this means is that if one person tries to import a module using one version of Swift, and that module was created by another version of Swift, their compiler can’t understand it, and they won’t be able to use it.

Well, in order to solve this version lock, Xcode 11 introduces a new format for Swift Modules, called Swift Module Interfaces. And just like the Compiled Module Format, they list out all the public APIs of a module, but in a textual form that behaves more like source code. And since they behave like source code, then future versions of the Swift Compiler will be able to import module interfaces created with older versions. And when you enable Build Libraries for Distribution, you’re telling the compiler to generate one of these stable interfaces whenever it builds your framework

Links for Xcode

Issue #499

Build setting

Build Library For Distribution

It turns on all the features that are necessary to build your library in such a way that it can be distributed

What does this error actually mean? Well, when the Swift compiler goes to import a module, it looks for a file called the Compiled Module for that library. If it finds one of these files, it reads off the manifest of public APIs that you can call into, and lets you use them. Now, this Compiled Module Format is a binary format that basically contains internal compiler data structures.

And since they’re just internal data structures, they’re subject to change with every version of the Swift Compiler. So what this means is that if one person tries to import a module using one version of Swift, and that module was created by another version of Swift, their compiler can’t understand it, and they won’t be able to use it.

Well, in order to solve this version lock, Xcode 11 introduces a new format for Swift Modules, called Swift Module Interfaces. And just like the Compiled Module Format, they list out all the public APIs of a module, but in a textual form that behaves more like source code. And since they behave like source code, then future versions of the Swift Compiler will be able to import module interfaces created with older versions. And when you enable Build Libraries for Distribution, you’re telling the compiler to generate one of these stable interfaces whenever it builds your framework

How to use new APIs in iOS

Issue #313

iOS 10

UserNotifications

Push user-facing notifications to the user’s device from a server, or generate them locally from your app.

UIViewPropertyAnimator

A class that animates changes to views and allows the dynamic modification of those animations.

NSPersistentContainer

A container that encapsulates the Core Data stack in your app.

UIFeedbackGenerator

The abstract superclass for all feedback generators.

iOS 10.3

SKStoreReviewController

An object that controls the process of requesting App Store ratings and reviews from users.

iOS 11

safeAreaLayoutGuide

The layout guide representing the portion of your view that is unobscured by bars and other content.

CoreML

Integrate machine learning models into your app.

Vision

Apply computer vision algorithms to perform a variety of tasks on input images and video.

ARKit

Integrate iOS device camera and motion features to produce augmented reality experiences in your app or game.

DeviceCheck

Access per-device, per-developer data that your associated server can use in its business logic.

Drag and Drop

Bring drag and drop to your app by using interaction APIs with your views.

CoreNFC

Detect NFC tags and read messages that contain NDEF data.

maskedCorners

Animatable corner radius

iOS 12

AuthenticationServices

Make it easy for users to log into apps and services.

iOS 13

SwiftUI

Declare the user interface and behavior for your app on every platform.

Combine

Customize handling of asynchronous events by combining event-processing operators.

CryptoKit

Perform cryptographic operations securely and efficiently.

UISearchTextField

Expose UISearchTextField on UISearchBar

MetricKit

➕CoreSVG

Links for WWDC

Issue #313

iOS 10

UserNotifications

Push user-facing notifications to the user’s device from a server, or generate them locally from your app.

UIViewPropertyAnimator

A class that animates changes to views and allows the dynamic modification of those animations.

NSPersistentContainer

A container that encapsulates the Core Data stack in your app.

UIFeedbackGenerator

The abstract superclass for all feedback generators.

iOS 10.3

SKStoreReviewController

An object that controls the process of requesting App Store ratings and reviews from users.

iOS 11

safeAreaLayoutGuide

The layout guide representing the portion of your view that is unobscured by bars and other content.

CoreML

Integrate machine learning models into your app.

Vision

Apply computer vision algorithms to perform a variety of tasks on input images and video.

ARKit

Integrate iOS device camera and motion features to produce augmented reality experiences in your app or game.

DeviceCheck

Access per-device, per-developer data that your associated server can use in its business logic.

Drag and Drop

Bring drag and drop to your app by using interaction APIs with your views.

CoreNFC

Detect NFC tags and read messages that contain NDEF data.

maskedCorners

Animatable corner radius

iOS 12

AuthenticationServices

Make it easy for users to log into apps and services.

iOS 13

SwiftUI

Declare the user interface and behavior for your app on every platform.

Combine

Customize handling of asynchronous events by combining event-processing operators.

CryptoKit

Perform cryptographic operations securely and efficiently.

UISearchTextField

Expose UISearchTextField on UISearchBar

MetricKit

➕CoreSVG

Learning VoIP, RTP and SIP (aka awesome pjsip)

Issue #284

Before working with Windows Phone and iOS, my life involved researching VoIP. That was to build a C library for voice over IP functionality for a very popular app, and that was how I got started in open source.

The library I was working with were Linphone and pjsip. I learn a lot of UDP and SIP protocol, how to build C library for consumption in iOS, Android and Windows Phone, how challenging it is to support C++ component and thread pool in Windows Phone 8, how to tweak entropy functionality in OpenSSL to make it compile in Windows Phone 8, how hard it was to debug C code with Android NDK. It was time when I needed to open Visual Studio, Xcode and Eclipse IDE at the same time, joined mailing list and followed gmane. Lots of good memories.

Today I find that those bookmarks I made are still available on Safari, so I think I should share here. I need to remove many articles because they are outdated or not available anymore. These are the resources that I actually read and used, not some random links. Hopefully you can find something useful.

This post focuses more about resources for pjsip on client and how to talk directly and with/without a proxy server.

First of all

Here are some of the articles and open sources made by me regarding VoIP, hope you find it useful

VoIP overview

Voice over Internet Protocol (also voice over IP, VoIP or IP telephony) is a methodology and group of technologies for the delivery of voice communications and multimedia sessions over Internet Protocol (IP) networks, such as the Internet

  • Voice over IP Overview: introduction to VoIP concepts, H.323 and SIP protocol

  • Voice over Internet Protocol the wikipedia article contains very foundation knowledge

  • Open Source VOIP Software: this is a must read. Lots of foundation articles about client and server functionalities, SIP, TURN, RTP, and many open sources framworks

  • VOIP call bandwidth: a very key factor in VoIP application is bandwidth consumption, it’s good to not going far beyond the accepted limit

  • Routers SIP ALG: this is the most annoying, because there is NAT and many types of NAT, also router with SIP ALG

  • SIP SIMPLE Client SDK: introduction to SIP core library, but it gives an overview of how

SIP

The Session Initiation Protocol (SIP) is a communications protocol for signaling and controlling multimedia communication sessions in applications of Internet telephony for voice and video calls, in private IP telephone systems, as well as in instant messaging over Internet Protocol (IP) networks.

SIP server

  • Kamailio: this is the server that I used, and it plays well with lots of standard SIP clients, including pjsip. Debugging on this server was also a fun story

RFC

RTP, SIP clients and server need to conform to some predefined protocols to meet standard and to be able to talk with each other. You need to read RFC a lot, besides you need to read some drafts.

NAT

NAT solves the problem with lack of IP, but it causes lots of problem for SIP applications, and for me as well 😂

TCP

Learn how TCP helps SIP in initiating session and to turn in TCP mode for package sending

TLS

Learn about Transport Layer Security and SSL, especially openSSL for how to secure SIP connection. The interesting thing is to read code in pjsip about how it uses openSSL to encrypt messages

ICE

Learn about Interactive Connectivity Establishment, another way to workaround NAT

STUN and TURN

Learn about Session Traversal Utilities for NAT and Traversal Using Relays around NAT, another way to workaround NAT

ALG

Learn about [Application Layer Gateway](http://Application Layer Gateway) and how it affects your SIP application. This component knows how to deal and modify your SIP message, so it might introduce unexpected behaviours.

Voice quality

Learn about voice quality, bandwidth and fixing delay in audio

Echo

This is a very common problem in VoIP, sometimes we hear voice from the other and also from us. Learn how echo is made, and how to effectively do echo cancellation

Dual Tone

Learn how to generate dual tone to make signal in telecommunication

pjsip

PJSIP is a free and open source multimedia communication library written in C language implementing standard based protocols such as SIP, SDP, RTP, STUN, TURN, and ICE. It combines signaling protocol (SIP) with rich multimedia framework and NAT traversal functionality into high level API that is portable and suitable for almost any type of systems ranging from desktops, embedded systems, to mobile handsets.

Threading

pjsip uses Local Thread Storage which introduces very cool behaviors

Resampling

How to work with sample rate of the media stream

Memory and Performance

Audio

Video

I learn a lot regarding video capture, ffmpeg and color space, especially YUV

CSipSimple

There are many SIP client for mobile and desktop, microSIP, Jitsi, Linphone, Doubango, … They all follow strictly SIP standard and may have their own SIP core, for example microSIP uses pjsip, Linphone uses liblinphone, …

Among that, I learn a lot from the Android client, CSipSimple, which offers very nice interface and have good functionalities. Unfortunately Google Code was closed, so I don’t know if the author has plan to do development on GitHub.

I also participated a lot on the Google forum for user and dev. Thanks for Regis, I learn a lot about open source and that made me interested in open source.

You can read What is a branded version

I don’t make any money from csipsimple at all. It’s a pure opensource and free as in speech project.
I develop it on my free time and just so that it benefit users.
That’s the reason why the project is released under GPL license terms. I advise you to read carefully the license (you’ll learn a lot of things on the spirit of the license and the project) : http://www.gnu.org/licenses/gpl.html
To sump up, the spirit of the GPL is that users should be always allowed to see the source code of the software they use, to use it the way they want and to redistribute it.

RTP Proxy

Because of NAT or in case users want to talk via a proxy, then a RTP proxy is needed. RTPProxy follows standard and works well with Kamailio

IP change

IP change during call can cause problem, such as when user goes from Wifi to 4G mode

RTP and RTCP

Learn about [Realtime transport control protocol](http://Real-time Transport Protocol) and how that works with RTP

Codec

To reduce payload size, we need to encode and decode the audio and video package. We usually use Speex and Opus. Also, it’s good to understand the .wav format

Building pjsip for Windows Phone 8

Windows Phone 8 introduces C++ component , changes in threading, VoIP and audio background mode. To do this I need to find another threadpool component and tweak openSSL a bit to make it compile on Windows Phone 8. I lost the source code so can’t upload the code to GitHub 😢. Also many links broke because Nokia was not here any more

Porting OpenSSL to Windows Phone 8

Firstly, learn how to compile, use OpenSSL. How to call it from pjsip, and how to make it compile in Visual Studio for Windows Phone 8. I also learn the important of Winsock, how to port a library. I struggled a lot with porting openSSL to Windows RT, then to Windows Phone 8

A lot of links were broken 😢 so I can’t paste them all here.

C and C++

Since pjsip, rtpproxy and kamailio are all C and C++ code. I needed to have a good understanding about them, especially pointer and memory handling. We also needed to learn about compile flags for debug and release builds, how to use Make, how to make static and dynamic libraries.

20 recommended utility apps for macOS

Issue #274

Original post https://hackernoon.com/20-recommended-utility-apps-for-macos-in-2018-ea494b4db72b


Depending on the need, we have different apps on the mac. As someone who worked mostly with development, below are my indispensable apps. They are like suits to Tony Stark. Since I love open source apps, they have higher priority in the list.

Open source apps

iTerm 2 https://www.iterm2.com/

iTerm2 is a replacement for Terminal and the successor to iTerm. It works on Macs with macOS 10.10 or newer. iTerm2 brings the terminal into the modern age with features you never knew you always wanted.

iTerm2 has good integration with tmux and supports Split Panes

Term2 allows you to divide a tab into many rectangular “panes”, each of which is a different terminal session. The shortcuts cmd-d and cmd-shift-d divide an existing session vertically or horizontally, respectively. You can navigate among split panes with cmd-opt-arrow or cmd-[ and cmd-]. You can “maximize” the current pane — hiding all others in that tab — with cmd-shift-enter. Pressing the shortcut again restores the hidden panes.

There’s the_silver_searcher with ag command to quickly search for files

oh-my-zsh https://github.com/robbyrussell/oh-my-zsh

A delightful community-driven (with 1,200+ contributors) framework for managing your zsh configuration. Includes 200+ optional plugins (rails, git, OSX, hub, capistrano, brew, ant, php, python, etc), over 140 themes to spice up your morning, and an auto-update tool so that makes it easy to keep up with the latest updates from the community.

I use z shell with oh-my-zsh plugins. I also use zsh-autocompletions to have autocompletion like fish shell and z to track and quickly navigate to the most used directories.

spectacle https://github.com/eczarny/spectacle

Spectacle allows you to organize your windows without using a mouse.

With spectable, I can organise windows easily with just Cmd+Option+F or Cmd+Option+Left

insomnia https://github.com/getinsomnia/insomnia

Insomnia is a cross-platform REST client, built on top of Electron.

Regardless if you like electron.js apps or not. This is a great tool for testing REST requets

Visual Studio Code https://github.com/Microsoft/vscode

VS Code is a new type of tool that combines the simplicity of a code editor with what developers need for their core edit-build-debug cycle. Code provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools.

This seems to be the most popular for front end development, and many other things. There’s bunch of extensions that make the experience to a new level.

IconGenerator https://github.com/onmyway133/IconGenerator

Built by me. When developing iOS, Android and macOS applications, I need a quick way to generate icons in different sizes. You can simply drag the generated asset into Xcode and that’s it.

vmd https://github.com/yoshuawuyts/vmd

Preview markdown files in a separate window. Markdown is formatted exactly the same as on GitHub.

colorpicker https://github.com/Toinane/colorpicker

A mininal but complete colorpicker desktop app

I used to use Sip but I often get the problem of losing focus.

GifCapture https://github.com/onmyway133/GifCapture

I built this as a native macOS app to capture screen and save to gif file. It works like Licecap but open source. There’s also an open source tool called kap that is slick.

Itsycal https://github.com/sfsam/Itsycal

Itsycal is a tiny calendar for your Mac’s menu bar.

The app is minimal and works very well. It can shows calendar for integrated accounts in the mac.

PushNotifications https://github.com/onmyway133/PushNotifications

I often need to test push notification to iOS and Android apps. And I want to support both certificate and key p8 authentication for Apple Push Notification service, so I built this tool.

Lyrics https://github.com/onmyway133/Lyrics

A menu bar app to show the lyric of the playing Spotify song

When I listen to some songs in Spotify, I want to see the lyrics too. The lyrics is fetched from https://genius.com/ and displayed in a wonderful UI.

gitify https://github.com/manosim/gitify

GitHub Notifications on your desktop.

I use this to get real time notification for issues and pull requests for projects on GitHub. I hope there is support for Bitbucket soon.

FinderGo https://github.com/onmyway133/FinderGo

FinderGo is both a native macOS app and a Finder extension. It has toolbar button that opens terminal right within Finder in the current directory. You can configure it to open either Terminal, iTerm2 or Hyper

Atom one dark theme

This is about theme. There is the very popular dracular themes, but I find it too strong for the eyes. I don’t use Atom, but I love its one dark UI. I used to maintain my own theme for Xcode called DarkSide but now I use xcode-one-dark for Xcode and Atom One Dark Theme for Visual Studio Code.

I also use Fira Code font in Xcode, Visual Studio Code and Android Studio, which has beautiful ligatures.

Chrome extensions

I use Chrome for its speed and wonderful support for extensions. The extensions I made are github-chat to enable chat within GitHub and github-extended to see more pinned repositories.

There are also refined github, github-repo-size and octotree that are indispensable for me.

caprine https://github.com/sindresorhus/caprine

Caprine is an unofficial and privacy focused Facebook Messenger app with many useful features.

Close source and commercial apps

Sublime Text https://www.sublimetext.com/

Sublime Text is a sophisticated text editor for code, markup and prose. You’ll love the slick user interface, extraordinary features and amazing performance.

Sublime Text is simply fast and the editing experience is very good. I’ve used Atom but it is too slow.

Sublime Merge https://www.sublimemerge.com/

Meet a new Git client, from the makers of Sublime Text

Sublime Merge never lets me down. The source control app is simply very quick. I used SourceTree in the past, but it is very slow and had problem with authentication to Bitbucket and GitHub, and it halts very often for React Native apps, which has lots of node modules committed.

1 Password https://1password.com/

1Password remembers them all for you. Save your passwords and log in to sites with a single click. It’s that simple.

Everyone need strong and unique passwords these day. This tool is indispensable

Monosnap https://monosnap.com/welcome

Make screenshots. Draw on it. Shoot video and share your files. It’s fast, easy and free.

I haven’t found a good open source alternative, this is good in capturing screen or portion of the screen.

VLC https://www.videolan.org/index.nb.html

iTunes or Quick Time has problem with some video codecs. This app VLC can play all kinds of video types.

Xcode https://developer.apple.com/xcode/

Xcode is the go to editor for iOS developer. The current version is Xcode 10. From Xcode 8, plugins are not supported. The way to go is Xcode extensions.

I have developed XcodeColorSense2 to easily recognise hex colors, and XcodeWay to easily navigate to many places right from Xcode

Sketch https://www.sketchapp.com/

Sketch is a design toolkit built to help you create your best work — from your earliest ideas, through to final artwork.

Sketch is the most favorite design tool these days. There are many cool plugins for it. I use Sketch-Action and User Flows

Where to go from here

I hope you find some new tools to try. If you know other awesome tools, feel free to make a comment. Here are some more links to discover further

Useful git commands for everyday use!

Issue #239

Original post https://medium.com/flawless-app-stories/useful-git-commands-for-everyday-use-e1a4de64037d


Do you know that questions about git get the most views on StackOverflow? I’ve searched a lot on Google how to execute certain actions with git, and this actually slowed me down a lot. There are some actions that we tend to use a lot, so it’s good to learn them. Here are my favorites, learning from friends and internet, hope you find them useful.

Before we begin, you should run git –version to check your current git version, mine is 2.12.2 as in macOS High Sierra. Here is the official git documentation, you can read details about git commands, parameters and new releases of git.

Useful commands

Diff

1
2
git diff feature/my_branch develop > file.diff
git apply file.diff

🔍 Status

Check the status of working directory and staging area:

git status

Show changes between HEAD and working directory:

git diff

Show the list of commits in one line format:

git log --oneline

Show commits that make add or remove a certain string:

git log -S 'LoginViewController'

Search commits that contain a log message:

git log — all — grep=’day of week’

🔍 Tag

List all tags:

git tag

Tag a commit:

git tag -a 1.4 -m "my version 1.4"

Delete remote tags:

git push --delete origin tagname

git push origin :tagname

Push tag to remote:

git push origin tagname

Rename tag:

git tag new old
git tag -d old
git push origin :refs/tags/old
git push --tags

Move tag from one commit to another commit:

git push origin :refs/tags/<tagname>
git tag -fa tagname
git push origin master --tags

🔍 Remote

List all remote:

git remote

Rename remote:

git remote rename old new

Remove stale remote tracking branches:

git remote prune origin

🔍 Branch

List all branches:

git branch

Create the branch on your local machine and switch in this branch:

git checkout -b branch_name

Create branch from commit:

git branch branch_name sha1_of_commit

Push the branch to remote:

git push origin branch_name

Rename other branch:

git branch -m old new

Rename current branch:

git branch -m new

Rename remote branch:

git branch -m old new               # Rename branch locally    
git push origin :old                 # Delete the old branch    
git push --set-upstream origin new   # Push the new branch, set local branch to track the new remote

Delete a branch:

git branch -D the_local_branch

git push origin :the_remote_branch

Delete all local branches but master

git branch | grep -v "master" | xargs git branch -D

🔍 Commit

Undo last commit:

git reset --hard HEAD~1

Squash last n commits into one commit:

git rebase -i HEAD~5

git reset --soft HEAD~5
git add .
git commit -m "Update"
git push -f origin master

Move last commits into new branch:

git branch newbranch
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.*1
git checkout newbranch

Make changes to older commit

1
2
3
4
git rebase -i HEAD^^^
// change from pick to edit, then :wq
git add .
git rebase --continue

🔍 Cherry Pick

Add some commits to the top of the current branch:

git cherry-pick hash_commit_A hash_commit_B

🔍 Reflog

Show reflog:

git reflog

Get commit:

git reset --hard 0254ea7

git cherry-pick 12944d8

🔍 Revert

Revert the previous commit:

git revert HEAD
git commit

Revert the changes from previous 3 commits without making commit:

git revert --no-commit HEAD~3..

🔍 Amend

Amend previous commit:

git commit --amend

git commit --amend --no-edit

git commit --amend -m "New commit message"

Changing git commit message after push:

git commit --amend -m "New commit message"
git push --force <repository> <branch>

🔍 Checkout

Checkout a tag:

git checkout tagname

git checkout -b newbranchname tagname

Checkout a branch:

git checkout destination_branch

Use -m if there is merge conflict:

git checkout -m master // from feature branch to master

Checkout a commit:

git checkout commit_hash

git checkout -b newbranchname HEAD~4

git checkout -b newbranchname commit_hash

git checkout commit_hash file

Checkout a file:

git checkout c5f567 -- Relative/Path/To/File

🔍 Stash

Save a change to stash:

git stash save "stash name"

git stash

List all stashes:

git stash list

Apply a stash:

git stash pop

git stash apply

git stash apply stash@{2}

🔍 Rebase

Rebase the current branch onto master:

git rebase master // rebase the current branch onto master

Continue rebase:

git rebase --continue

Abort rebase:

git rebase --abort

🔍 .gitignore

Un-track files that have just been declared in .gitignore:

git rm -r --cached .
git add .
git commit -am "Remove ignored files"

🔍 Index

Remove untracked files:

git clean

Remove file from index:

git reset file

Reset the index to match the most recent commit:

git reset

Reset the index and the working directory to match the most recent commit:

git reset --hard

🔍 Misc

Get their changes during git rebase:

git checkout --ours foo/bar.java
git add foo/bar.java

Get their changes during git merge:

git pull -X theirs

git checkout --theirs path/to/the/conflicted_file.php

git checkout --theirs .
git add .

git checkout branchA
git merge -X theirs branchB

Merge commits from master into feature branch:

git checkout feature1
git merge --no-ff master

Find bug in commit history in a binary search tree style:

git bisect start

git bisect good

git bisect bad

Git alias

If there are commands that you use a lot, then consider using git alias. This is how to make alias for git status, then you can just type git st:

git config — global alias.st status

Alias configurations are stored in .gitconfig file, you can learn some cool aliases from thoughtbot and mathiasbynens.

Delete all local branches except master

1
git config --global alias.dlb '!git branch | grep -v "master" | xargs git branch -D'

Prune upstream branches

1
git config --global alias.pu 'remote prune upstream'

GUI clients

Doing things in command line is cool and faster. However for viewing branches and commits, I find using a GUI client more visualizing and comfortable. You can see a list of all GUI clients here, I myself use SourceTree.

Check before you commit

We usually have some experiment code that we don’t want they to step into our commit. I usually mark my experiment with // but sometimes forget to unstage that.

Starting with 2.9, Git has improvement on its commit hook which makes it globally using hooksPath.

Firstly we nee to create a file called pre-commit, and place it into, for example, /Users/khoa/hooks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh

# https://appventure.me/2016/04/04/prevent-accidental-test-code-commits/

if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# The special marker tag to mark things which we still need to change
marker="<TEST>"

# Redirect output to stderr.
exec 1>&2

if test $(git diff --cached -z $against | grep $marker | wc -c) != 0
then
cat <<\EOF
Error: Still has invalid debug markers in code:
EOF
echo `git diff --cached -z $against -G $marker`
exit 1
fi

In your project, run git config core.hooksPath /Users/khoa/hooks.

Whenever you commit a file with that pattern, it won’t let you commit. For how to make this work in SourceTree, check:
SourceTree and pre commit hook
Pre-commit file works perfectly in terminal, but SourceTree seems to ignore it. I use both terminal and SourceTree, as…medium.com

Where to go from here

This is just scratching the surface of what git can do, if you want to learn more, here are some links to get started.