Wilson Mar bio photo

Wilson Mar

Hello!

Calendar YouTube Github

LinkedIn

Here is a quick way to learn Git version control for contributing to open source

US (English)   Norsk (Norwegian)   Español (Spanish)   Français (French)   Deutsch (German)   Italiano   Português   Estonian   اَلْعَرَبِيَّةُ (Egypt Arabic)   Napali   中文 (简体) Chinese (Simplified)   日本語 Japanese   한국어 Korean

Overview

Git Basics video


Hello, I’m Wilson Mar [@wilsonmar], It was 2010 when, after years of working on what came before Git, I knew Git would become essential for developers and testers. Since 2014, I have been holding live, full-day hands-on tutorials at conferences because skill with Git and Linux commands are prerequisites to many IT jobs.

Along the way, I devoured every video and book I could find on the subject and listed them in my blog.

I also created tutorials on how to install Git clients and hook it up to repositories in the cloud.

Throughout this course, we are on a hunt to answer these questions about working with code.

NOTE: Content here are my personal opinions, and not intended to represent any employer (past or present). “PROTIP:” here highlight information I haven’t seen elsewhere on the internet because it is hard-won, little-know but significant facts based on my personal research and experience.

You’ll learn in minimum time because I’ve spent a lot of time on sequencing your learning and on time-saving automation.

The focus of this course is on helping you to confidently contribute to open source, using techniques and utilities that top professionals use.

This means we often use commands to verify status after key steps. Verification steps are part of bash scripts I’ve created so that you can tell, on your own, whether you’re typed incorrectly or whether there’s some issue with the system. Having a script means you can change something to see how it affects the whole workflow. The scripts are a very unique aspect that I haven’t seen elsewhere.

Another special aspect of this course are the keyboard shortcuts pros use to boost their efficiency. At the end of each segment, we review key learnings to make sure you have mastered each skill.

In this tutorial, we also learn how to recover from mistakes right after we try each command.

Basic concepts video

NOTE: Since the above video was created, GitHub has gone to free private repositories, but no longer free website hosting. Their $4 a month for “Pro” subscriptions does provide GitHub Pages hosting.

To recap the video, a “blob” is the set of characters in the last revision identified by a “hash” calculated from the text changed plus some metadata such as the data of the change, the person making the change, etc. The 7 characters shown are the first 7 characters of the full hash string.

Git Clients

There are several technologies you can use to work with Git version control:

  • Gitless is a Git-compatible version control system, that is easy to learn and use.

Script and Topics flowchart

Several others have created “cheat sheets”, but they are structured like a dictionary or according to how the program works, not the sequence of how you work.

To start off, let’s look at the major chapters, marked by red numbered dots. The first diagram is about installation and configuration:

git-basics-10d-flow-1215x683.jpg
Click on the flowchart above to pop-up a full-screen (static) image

For an animated flowchart where you can click the Pause button or drag the timing bubble on the video’s timeline while watching:

BTW I’m working on a video in which I verbally narrate the video using text on this web page. Until then I have for you an animated diagram above.

Terminal or Git Bash Console

This tutorial is designed for someone with a Git command-line client installed.

To make use of a terminal console on your laptop, you need to open the Terminal program on a Mac or, if you’re on a Windows machine, the Git Bash program installed.

Since a Bash shell does not come with Microsoft Windows machines, a bash terminal program called git bash needs to be installed by the Windows Chocolatey installer installing the msysgit package. But you must have Administrator permissions to install them.

On Windows:

  1. So that you can update versions automatically later, automate client installs on Windows, by first installing Chocolatey using a PowerShell command:

    <@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
    
  2. While you’re on a command window, have Chocolatey install a Git client:

    choco install msysgit -y

    -y takes the place of clicking “OK” to submit to legalistic licenses.

  3. To open git bash, press the Micosoft icon on the keyboard. Type git. When “git bash” appears, click on it.

    image

On macOS (Mac laptop):

  1. Open a Bash terminal by command+Tab (holding down the command key, then press Tab) for the Spotlight. Type “ter” until “Terminal.app” appears, press Enter to select it.

  2. Click anywhere on the Terminal window. Press command+N for the Basic session colors and other UI. Alternately, point your cursor beyond the top of the screen for the Terminal menu to appear and select Shell, New Window and select one of the themes.

    git-terminal-colors-689x273.jpg

    • Grass is green
    • Ocean is blue
    • Pro is black
    • Red Sands is earthy brown

    Terminal vs GUI

    Due to the open-source availability to how Git works, there are many Git clients available.

    Most IDEs and text editors today have incorporated commonly used Git client functionality into their software.

    But NOT all clients include a git bash program so you can run commands like a Linux command line (shown by this tutorial).

    The IntelliJ IDE has a Git menu by right-clicking on the project in the left Solution pane to see this menu:

    We will return to this after going through this course to see if you recognize what each GUI item does.

    git-intellij-648x243.jpg

    In Eclipse, right-click on the project in the left Solution pane, then select Team for the Git menu:

    git eclipse menu 518x648

    • The “Switch To” in Eclipse is the equivalent of git checkout. This avoids confusion with the “checkout” function in Subversion, which does something completely different than git checkout. Git does not isolate what is checked out nor lock portions of code like what Subversion does, which results in people waiting for others to finish. For this reason, many have migrated from Subversion to Git. We will soon show how Git enables every portion of code to be worked on at the same time.

    • See VIDEO: Using Git within Eclipse May 21, 2013 by Dr. Brian Fraser

Ways to get a Git repository

PROTIP: There are several ways to obtain a repository in GitHub, GitLab, BitBucket, or other host:

a. Download ZIP file
b. Open in Desktop client (using Git for GitHub)
c. git clone https or SSH url
d. hub command (installed)
e. bash curl command to a script (see immediately below)

A script types commands for you

A bash script was created to automatically type for you the various commands in this course. It’s to see whether what you are asked to type manually actually works. This is so we can see whether it was working before, so you don’t blame yourself for not typing commands correctly and waste time debugging.

  1. Let’s look at the script by providing an internet browser (such as Brave) with this full URL:

    https://github.com/wilsonmar/git-utilities/blob/master/git-basics.sh

    Follow along with me on your own machine.

    This file in GitHub.com is associated with an account named “wilsonmar”.

    The file is in a repository (or “repo”) named “git-utilities”.

    The “.sh” means it’s a shell script file.

    The first line inside each shell script specifies the program which processes it. In this case it’s

    #!/bin/bash

    which is the default shell program that comes with MacOS machines to process commands of the operating system.

    Bash curl command pasted in Terminal

    To run the shell script without first downloading it:

  2. Open a Terminal or Git Bash dialog.
  3. Run a Bash script is by pasting in a Terminal a bash curl copied from a web page. This places you at your account’s home directory such as, in my case:

    /Users/wilsonmar

    On your dialog, a different name would appear instead of “wilsonmar”.

  4. PROTIP: You can return to this folder by several commands: cd (change directory) command by itself or cd ~ (the tilde character), which resolves to cd $HOME.

  5. $HOME is a built-in variable for your account’s home directory.

    Shell scripts make use of built-in variables such as $HOME.

  6. There is also a built-in command pwd (present working directory), which is equivalent to the “dir” command on Windows machines.

    ./bash_profile

  7. PROTIP: When a Terminal or Git Bash session is started on a Mac, the operating system automatically (in the background) runs several files, which include:

    source ~/.bash_profile

    This places into memory definitions for the PATH of directories the operating system looks for the location of programs requested to run, for export of environment variables.

  8. The dot character in the file name means that the file is normally hidden, so additional parameters need to be specified to see them, such as:

    ls -al

    PROTIP: Don’t type the “[ll]” shown in the flowchart because that defines a keyboard alias which the bash_profile also defines them.

    git-basics.sh add aliases

  9. PROTIP: A set of aliases is defined at:

    https://github.com/wilsonmar/git-utilities/blob/master/aliases.txt

    You can copy and paste the raw text into your ./bash_profile open in a text editor.

    Or you can run a script to do that for you, described below.

    What follows are the steps to get the aliases to be available in your laptop, among other tasks.

    Remote bash command run

    To get you started quickly:

  10. Highlight this entire line (which may wrap around)

    bash -c "$(curl -fsSL https://raw.githubusercontent.com/wilsonmar/git-utilities/master/git-basics.sh)"

    (Position your mouse at the beginning of the string, drage the mouse as you drag across the line, then relase at the end of the line. Press command+C to copy to your Clipboard.)

  11. Press command+C to copy into your operating system’s Clipboard.
  12. Open a (Git-capable) Terminal or Git Bash session.
  13. Click on the blank part and right-click to select Paste.
  14. Press Enter to run it.

    In the output displayed on the Terminal:

    Lines prefixed by >>> output by function echo_f() precede each section and step of the script:

    Lines prefixed by $ output by function echo_c() precede each command:

index to git-basics.sh output

Numbering of sections in the script:

0.x Bash Script start-up (above)

1.x Git client Install and Configuration

2.x Git program Configuration

3.x Cloud repository setup, forking, cloning

4.x Branch and edit locally

5.x Local config, add, commit

6.x Local edit, add, commit, push

7.x Pull and Merge

8.x Update origin with upstream changes

9.x Get changes in origin to local

In outputs on Terminals, “$” precede commands issued by the script, so you can type the exact commands.

0.x Bash script start-up

The script displays the time and other metadata about the run.

  1. The first thing the script does is figure out what operating system it is running on. That uses a command common to all operating systems:

    uname -a

    NOTE: “Darwin” is the internal name of the operating system running on a Mac.

1.x Client Install and Configuration

If needed, the git-basics.sh script installs a Git client using a package manager.

On a Mac:

  1. If the script is running on a Mac, the script installs Homebrew if needed.

  2. A Git client is installed if the git command is not found.

    brew install git

    Its version is output so you know what you have.

  3. For the script to automatically create a repository in GitHub and fork a repository from another account, it first installs the “hub” add-in to Git:

    brew install hub

    BTW: There is no “cask” in the brew command because it only works within the command line, not GUI.

    PROTIP: Technically “Hub” is called a “wrapper” around Git’s CLI so you can do it all from the command line rather than switching to GitHub’s web page.

  4. Also install jq to enable Git to process JSON:

    brew install jq

    On Windows machines, the Chocolatey package manager is installed if not found.

    Environment variables & values

  5. File git-basics.env is downloaded to your $HOME account folder (by a curl command):

    curl -O "https://raw.githubusercontent.com/wilsonmar/git-utilities/master/git-basics.env"

    The file provides values to environment variables controlling the script. It is loaded into memory using this command:

    source git-basics.env

    The variables and the values are displayed.

    PROTIP: The git-basics.env file is placed in your $HOME folder, separate from any folder that can be pushed back to Git hosting because you may want to type your password in the file for more automatic functionality in the script, such as deleting repos, functions which even hub does not perform.

    There is an enterprise edition of the script that has more secure features such as reference to a Vault server which holds and dispenses secrets.

    git-scripts folder in $HOME

  6. The script looks for a persistent folder named git-scripts and creates it if it doesn’t exist there.

    Files from GitHub load the folder with files containing default values. If the file is there already, don’t overlay the file.

  7. git-basics.sh, the shell script described above is copied into that “git-utilities” folder so you have the option of editing the files and re-running locally on your laptop.

  8. To halt processing for customizations, press control+C on the Mac or Press any key to continue default processing.

    This is so you can edit the files downloaded so you can run rerun locally (as described below).

    If you do edit the file locally, comment out the read command line and its echo command by adding a # in front of them.

    Edit and rerun locally

    The script git-basics.sh is designed to both run without modification and to allow you to customize it. There are several reasons you want to customize files in the “~/git-utilities” folder the script added for you:

    • Edit the git-basics.env to place exit anywhere you want the script to stop.

    • Edit the git-basics.env file to replace “secret here” with your GitHub password so that the script can automatically delete repos locally, so that you don’t have to manually do it every time you run. PROTIP: The git-basics.env file is copied to a separate folder than the script so that you never upload it back to a Git hosting GitHub/GitLab.

    • Adopt the git-basics.sh to automate other activities using Git. PROTIP: Select a text editor. See my notes at https://wilsonmar.github.io/text-editors

    chmod

    PROTIP: To avoid error messages that says the file is not there, change premissions to enable the script file to be executed

    chmod +x git-basics.sh

    To run the file, type a “./” in front of the script file name:

    ./git-basics.sh

    Continuing processing

  9. aliases.txt containing keyboard shortcut definitions are copied in. Again, if the file is there already, it is not overlaid.

  10. If a ~/.bash_profile is not found, the script creates it in your $HOME folder.

  11. Whenever a Bash terminal session is opened, it automatically runs the ~/.bash_profile script file at the user home folder. The “~/” specifies that the file is in the user’s $HOME folder.

The file contains a definition of the PATH the operating system searches for executables.

The file can also contain custom keyboard commands defined by alias specifications. The alias.txt file.

The aliases.txt file is concatenated to the bottom of the ~/.bash_ profile

  1. The script creates a volatile/non-persistent workspace folder which, when configured, the script deletes at the beginning of each run, and populates again with downloads.

    The name of the volatile folder is based on the variable $WORKSPACE_FOLDER defined in file git-basics.env. Thus, if you may the value of the variable, a different workspace would be created on the next run.

    This is what enables the script to be run over and over again.

  2. Before invoking a Git command, load the default SSH public key file to make sure it is available for use with “hands-free” GitHub API calls:

export RSA_PUBLIC_KEY=$(cat ~/.ssh/id_rsa.pub)

2.x Git Global Config.

Git stores configuration commands in file .gitconfig in the user’s $HOME folder.

  1. Two commands Git requires is for attribution included in metadata for each commit. Type these commands, replacing the example for Wilson with your own name and email address:

    
    git config --global user.name "Wilson Mar"
    git config --global user.id "wilsonmar+GitHub@gmail.com"
    
  2. On a Windows machine, to mute annoying warnings about conversion of line endings in files saved on Mac machines:

    
    git config --global core.safecrlf false
    

    The command issues no response.

    The command does not turn off conversion.

  3. To list the global .gitconfig configuration file:

    
    git config --list
    

    PROTIP: If you see a : (colon) on the last line of the Terminal screen, press q to quit out of the listing.

    WARNING: This is commented out because it’s too long.

    In our class we don’t have the time to go through all the configurations that control how Git makes use of its many features.

    BONUS: More configuration settings

    Local Git projects container folder

  4. PROTIP: Optionally, create a folder with a name such as gits or “project” to hold anonymouse Git repositories from various GitHub/GitLab accounts. This would be immediately under your $HOME folder:

    cd $HOME && mkdir gits && cd gits
    
  5. PROTIP: Optionally, create an account container folder to hold repositories under each account on GitHub/GitLab or to group repositories relared to the same subject:

    mkdir myacct && cd myacct
    

    PROTIP CAUTION: Some repos have a 20 character limit on the prefix before the repo name. The character count includes slashes:

    /gits/hotwilson/----+----1----+----2

    Init repo

  6. PROTIP: If you are creating a local repository, first create an account or project container folder to hold the various repositories.

    mkdir local-init && cd local-init
    
  7. The git init command creates a blank Git repository:

    git init
    

    The sample response:

    Initialized empty Git repository in /Users/kevingrastorf/git-basics-workspace/gits/myacct/local-init/.git/
    

    This creates in the current folder a folder always named .git into which Git stores and retrieves all change history.

  8. Add contents:

    git add --all
    git commit -m "First commit"
    
  9. Delete the “local-init” repo created by a previous run on GitHub. This requires use of the GitHub API because “hub delete” does no work.

    BONUS: See the GitHub API description

  10. Although GitHub docs say that you need to first create repos in the GUI. However, the Hub add-in enables it:

    hub create -d "My new thing"

This is instead of

  • git remote add origin remote repository URL
  • git push -u origin master

  1. git remote -v
  2. Manually check on GitHub to make sure it’s really there.

3.x Cloud repository setup

Now let’s talk about working with repositories in cloud hosts such as Microsoft’s GitHub, GitLab, Atlassian’s BitBucket, etc.

Identify a public GitHub repository and something that you can contribute to, such as suggesting a typo fix. But you have no right to edit that repo. For example:

https://github.com/hotwilson/some-repo

An “open source” repository available to the “public” can be accessed using an URL using https protocol even if you are not a designated collaborator with a right to alter it.

Such a repository we call an upstream remote or some other name.

You can click the “Star”, but you would need to be signed in under your own account.

We assume here that you have already opened a personal email account and used it to create an account on GitHub.com.

PROTIP: I use email accounts on both Gmail.com and Outlook.com so I can test sending emails.

PROTIP: It helps if you pick a unique handle that is available throughout various social media (Twitter, Medium, Instagram, etc.)

Setup SSH manually

Before you can “Fork” another account’s repository, you need to enable your Git commands to have right to modify.

The right to alter a cloud repo is claimed by pasting the contents of your machine’s .pub (public key file) obtained by using pbcopy to put it in your Mac’s Clipboard, then pasted in the cloud GitHub/GitLab GUI.

The “rsa” in the default file name “id_rsa.pub” refers to the clever mathematicians who figured out how to encrypt and decrypt files without exchanging secret passwords. It works by generating two files – the public and private key file “id_rsa” – into your account’s ~/.ssh folder.

SSH (for secure shell) is the same mechanism the Linux team uses to secure its bash terminals. In fact, Git was designed by Linus Torvold and his team who developed the Linux kernel and its secure shell (SSH).

The ssh-keygen program that generates the rsa files comes with all Linux and Mac operating systems. First cd to the .ssh folder, then run the program. If you are using it for the first time, press Enter for all the defaults.

When there are multiple accounts, you can’t use default names and also must edit the config file to make sure the different accounts are specified correctly.

Windows users need to download the puttygen program.

When the Git client runs, it retrieves the private key in the id_rsa file to encrypt what it sends. The GitHub or GitLab cloud service decrypts using the public key.

  1. When this script runs, one can optionally use the built-in command “cat” to retrieve the contents of the public key for later use by the GitHubAPI:

    export RSA_PUBLIC_KEY=$(cat ~/.ssh/id_rsa.pub)
  2. Alternately, list the files in folder .ssh to confirm the file size:

    ls -a ~/.ssh
    

    Two-factor authentication and repo URLs

    Next, instead of git init creating a purely local repository and its need for being in a repository folder, we now look at use of git clone commands to bring in remote repositories from the Cloud to your local machine.

    When pulling in a repository belonging to some other account you don’t have right to change, use the “HTTPS” URL format, such as:

    https://github.com/hotwilson/some-repo

    PROTIP: If we have setup two-factor authentication (2FA), we should use the “SSH” or “git@” form to specify URLs we have right to change, such as:

    git@github.com:wilsonmar/some-repo

    We should use two-factor because it is good security practice. The “two” means we use an alternate form of identification, in a number from an authentictor app (such as Google Authenticator) on that smart phone you’re always looking at.

    PROTIP: –depth=1

    BTW: When cloning another account’s repo, some add –depth=1 or –recursive

    • --depth=1 says only clone the latest version, to save disk space locally by not having previous version history (nor branches) on your machine.

    • --recursive says to pull in sub-modules (repositories stored within the repository).

    Fork from another repository

    Since our objective is to work on open source repositories we don’t own, we now bring in a repository from another account we don’t own.

    VIDEO: On the GitHub/GitLab website, we can manually click fork to bring it under our own account. We would then have a right to change the our own repository.

    Fork using Hub add-in to Git

    BLOG: Instead of doing that manually, we have a way to do that in a command-line script.

  3. The forked repository created from a previous run needs to be first deleted from GitHub/GitLab, manually because there is no “hub delete repo”.

    Click Settings tab. Scroll down to click “Delete this repository”. Type the repo name. Click the red “I understand…”.

    [ Whoops ]

    PROTIP: Click “Whoops” links for instructions on un-doing the commands immediately before the link. There is a “Return” link there to get back here and continue.

    Un-comment the command to pause to read your response of pressing any key after doing the action stated.

  4. Use hub to clone before forking.

    cd && cd "$WORKSPACE_FOLDER"

    PROTIP: Instructions at https://hub.github.com assume you added an alias with a command such as “alias git=hub”. So I say don’t do that because I only use Hub just for forking. So use “hub” instead of “git” in commands.

    Hub expects the upstream remote to be named “origin”, so first clone the other repo:

    hub clone hotwilson/some-repo

    Hub creates the repo with remote of “origin”. That’s counter to how we’re using that remote name in this script. But hub requires it for its fork command:

    hub fork hotwilson/some-repo

    You may need to provide your cloud account credentials if they are not cached.

    Hub’s fork command returns:

    Updating wilsonmar
    From git://github.com/hotwilson/some-repo
            * [new branch]      master     -> wilsonmar/master
    new remote: wilsonmar
    

    To make the remote names the way I want:

    hub remote add myself
    git remote rename myself origin
    git pull --all
    

    The git pull –all command returns this:

    Fetching upstream
    Fetching origin
    Already up to date.
    

    BONUS: Set default branch

  5. Verify using a git remote -v to show it looks like this:

    origin   git@github.com:wilsonmar/some-repo.git (fetch)
    origin   git@github.com:wilsonmar/some-repo.git (push)
    upstream git://github.com/hotwilson/some-repo.git (fetch)
    upstream git://github.com/hotwilson/some-repo.git (push)
    
  6. If you want, manually see the new fork on your cloud account at:
    https://github.com/wilsonmar?tab=repositories

    You may want to un-comment the command to pause to read your response of pressing any key after doing the action stated.

    Clone

  7. When a repository is cloned, Git automatically creates that repo’s folder and within it a folder named dot git to hold objects that track changes to the repository.

    During cloning, Git automatically extracts files from objects in its history, such as README.md out to the repository’s Working Directory, as if you typed git checkout master.

    [ Whoops ]

    So remember to cd into the repository folder:

    cd some-repo
    

    PROTIP: Forgetting to cd into a newly cloned folder is a common mistake.

  8. For a listing of repository files and folders in the Git working directory for the current git checkout, which is “master” when first initialized:

    ls -al
    
  9. List the remotes:

    git remote -v
    

    This information is also presented by the git branch command.

    Branch list

  10. A git branch command shows us the branches defined in the repository (in a read-only operation):

    git branch -avv

    The -avv parameter provides more detail. Dash a specifies remote tracking branches to appear as well.

    Alternately, shortcut:

    gb

    An example of the response:

            * master                6110cb1 [origin/master] Update ...
      remotes/origin/master 6110cb1 Update ...
    
    • The asterisk (*) marks the current branch – master, the default branch name.

    • 6110cb1 in this example is the SHA hash prefix of the last commit made.

    • The default remote is origin with default branch master.

    Alternatively, identify just the current remote and branch using this command:

    git symbolic-ref --short HEAD

    BTW: A Git alternative to the Bash “pwd” (present working directory) command is:

    git rev-parse --show-toplevel



    PROTIP: Git defaults to the master branch. But many organizations protect that name for production use, and instead create a “development” or “dev” branch for developers to work with.


4.x Branch and edit locally

Here’s where you add value to that Open-Source repository.

The git checkout command controls what Git extracts out from the repository database to the repository’s Working Directory.

  • If you add a file after the git checkout command, Git will replace the file in the working folder with the version in the committed repository.

[ Whoops ]

New branch with commit

There are two ways to create a new branch.

  1. The easiest way is to specify a new branch name after a dash b (-g) with as part of a git checkout such as “feat1” (feature1), to associate new changes:

    git checkout -b feat1
    

    PROTIP: Atlassian defines its branches* with a type (feature, bugfix, hotfix, etc.), a slash, an issue number, then a short description.

    git atlassian branch naming 650x222

    The advantage of creating a branch is that parallel development can occur without risking the master branch.

    BTW: Unlike the Subversion client, which locks branches to all others, when a Git branch is created, all files are still available for change.

    [ Whoops ]

  2. List branches to compare with another git branch command:

    git branch -avv

    Notice the asterisk is now at the new branch “feat1”.

    Concatenate to .gitignore

  3. The script makes changes by using echo commands, such as:

    echo "peace" >newfile.md

    The single “>” clears out all contents and replaces it with that one word.

    echo -e "\n.DS_Store" >>.gitignore

    >> concatenates to the bottom of the file

    -e enables specification of escape characters

    \n` is an escape character for new line

    Alternately, you can of course edit files manually, we can use vim or another text editor (such as nano, atom, etc.) to change contents inside files.

    vim README.md
    

    The md in the file name designates markdown format. The README.md file Git hosts can create with this specific name to describe each repository.

    If you use vim, press the I key to begin insertion and press Esc to end insertion mode. When out of insertion mode, type : to enter command mode, then wq to write and quit the program or then q! to quit without changes.

    PROTIP: Remember to save the file before switching.

  4. Display the last 3 lines in the file to confirm:

    tail -3 .ignore

    This is better than using the cat program which displays all lines.

    git status -s -b [gsl]

  5. To detail the status of changes to the repo:

    git status -s -b

    Alternately, this command is used so often that Mac users create a shortcut in ~/.bash_profile:

    gsl

    The response is:

    \#\# feat1
     M .gitignore
    

    About the parameters:

    ## marks a branch

    M marks a file Modified from Git

    ?? marks a file untracked by Git, one that has never been added to Git.

    Hide file from git status

    EXTRA: PROTIP: To have git status not display a particular file:

    git update-index .DS_Store

    PROTIP: Changes to files that should not be pushed up, such as the secrets.sh file, should be marked:

    git update-index --no-skip-worktree secrets.sh

    NOTE: Pulls of changes upstream in GitHub updates the local file.

    If both the local and upstream file are changed, Git outputs a conflict message.

    Skipped files are flagged with a “S” in this list command:

    git ls-files -v|grep '^S'

5.x Local config, add, commit

  1. Before adding to git’s Staging for the current repository, you may want to configure the local attribution just for the current repository:

    cat .git/config

    The first part of the file are repository-specific configurations:

    [core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
    

    The second part:

    [remote "upstream"]
    url = git://github.com/hotwilson/some-repo.git
    fetch = +refs/heads/*:refs/remotes/upstream/*
    [branch "master"]
    remote = upstream
    merge = refs/heads/master
    [remote "origin"]
    url = git@github.com:wilsonmar/some-repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    

    Optionally, override the global attribution with this:

    
    git config          user.name "Wilson Mar"
    git config          user.id "wilsonmar+GitHub@gmail.com"
    

    This is done by issuing a git config command without the “–global” parameter.

    diff

  2. See the difference between the working area and staging:

    git diff

    See the difference between the staging and repository:

    git diff --staged

    See the difference between the working area and repository:

    git diff HEAD

    There should be no response when nothing has been put in Git staging.

    References:

    • https://git-scm.com/docs/git-diff
    • https://www.freecodecamp.org/news/git-diff-command/
    • https://blog.mergify.com/how-to-compare-two-branches-in-github/

  3. You can change several files, but only the files you add to Git’s staging area will be pushed to GitHub.

    git add . -A

    The dot selects all files changed, recursively inside sub-folders as well.

    The dash capital A parameter specifies that deleted files be processed.

    PROTIP: Many prefer to specifically add individual files to go into each particular commit, which works on all files added to staging. Instead of the dot, several files can be specified on the same command.

    Alternately, to add all the files modified (not new files created and untracked):

    git add -u

    Git has a “two-phase commit” approach. Files are added to Git’s index file. Then all files staged in the index are committed together by the git commit command.

    [ Whoops ]

  4. Do another diff to see how git add changed:

    git diff --cached
  5. Do a git status again:

    git status -s -b

    Notice the green “M” (for modifed) and “A” (for Add) showing it’s being tracked:

    M  .gitignore
    A  newfile.md
    

    git commit

  6. The commit supplies a message describing changes, which applies to all files added. For example:

    git commit -m"Add .DS_Store to .gitignore @hotwilson"
    • A space is not needed between the m and the left quote of the message.

    • If you don’t specify the dash m, Git will display a file containing comments as a prompt. In that file, any line beginning with the # comment character will be ignored and not be part of the message. Save and exit the text editor.

    • If a GitHub account user name prefixed by an at sign (such as “@wilsonmar”) is specified in the message, GitHub automatically sends an email.

    A sample response:

    [feat1 e2265f6] Update for show
     2 files changed, 3 insertions(+)
     create mode 100644 newfile.md
    



    [ Whoops ]

    What did I do? Local Reflog

  7. List history of actions that have occurred locally (on your laptop):

    git reflog -5

    -5 specifies only the most recent 5 lines.

    Example output:

    9349be6 HEAD@{1}: commit: Update
    9eb57bc HEAD@{2}: commit: mention gs shortcut 
    

    Each commit is put at the head of the chain of all commits made throughout history. That 7-characters is the prefix to a much longer “hash code” that Git generates from each commit. Content within Git is very difficult to alter because any change would result in a different hash. And insertions would break the chain of commits.

    • Locally, entries are pruned (removed) automatically after 90 days (by default).

    Find commit by message

  8. Retrieve a commit by searching by message text rather than by SHA hash:

    git show :/Update

    DOCS: The most recent commit containing “Update” will appear.

    Local Log

  9. List the last 10 commits:

    git log --pretty=format:"%h %s %ad" --graph --since=1.days --date=relative;git log --show-signature -n 1 | tail -n 10

    | “pipes” the result of one command into another command (tail).

    | tail -n 10 limits the output to 10 lines.

    Undelete

  10. List names of files deleted anywhere within the repository:**

    git log --diff-filter=D --summary | grep "delete mode"

    grep is a commonly used command to filter output to just lines containing the word specified.

    An example of the output:

    delete mode 100644 _layouts/product.html

    NOTE: An underline in front of file and folder names indicates those created by the program itself, not one added by users.

    PROTIP: cd to the root of the repository to reference the file path shown.

  11. List the hash of commits related to the file specified:

    git log --oneline --follow -- "_layouts/product.html"</pre>
    
    An example output:
    
    
    a21b79e Update museum image size
    The hash prefix (a21b79e) is for the commit which deleted the file.
  12. Un-delete the file using ^ (caret character) which specifies the commit just prior to the one specified in the command. For example:

    git checkout a21b79e^ -- "_layouts/product.html"

    Rebase to squash locally

  13. Before pushing to GitHub/GitLab, some prefer to squash some commits so that only one commit message appears for several commits made locally.

    • Rebasing cleans up intermediate commits that are unwanted noise to the rest of the team. The extra commits complicates the history, and makes back-out of code more difficult.

    • Yes, this changes history, which is why we do it. But it’s only on your local versions.

    git rebase -i

    -i for interactive

    My blog on rebasing at http://wilsonmar.github.io/git-rebase provides step-by-step instructions. Then return here.

6.x Push and Push with tags

  1. The git push command sends to a remote repo what has been committed for a specific branch. For the default origin remote and master branch:

    git push

    [ Whoops ]

    Alternately, Bash users who have setup an alias can use this which includes a git add and commit:

    gas “JIRA #1234”

    This alias adds all changes and commits them all with a static “update” in message:

    gbs

    The two aliases above include a git push for maximum convenience.

    GitHub Enterprise uses can push to multiple remotes with one command:

    git push production,staging

    If you see an error message like this, it means you don’t have permissions:

    ERROR: Permission to hotwilson/some-repo.git denied to wilsonmar.
    fatal: Could not read from remote repository.
    Please make sure you have the correct access rights
    and the repository exists.
    

    Two-factor authentication

    If you setup 2FA (two-factor authentication), Git will prompt you for a username and password. Because of 2FA, GitHub expects a personal access token. If you type in your account password, GitHub responds with “fatal: Authentication failed for https://github.com/…”.

    So when using 2FA, in a Terminal session, cd into .git, then edit file config to change, for example:

    url = https://github.com/wilsonmar/futures

    to

    url = git@github.com:wilsonmar/futures

    Notice the git user name instead of “https://”. Behind the scenes, SSH protocol is used, but that doesn’t need to be specified here.

    Also notice the colon character instead of the slash.

    cd back up out of .git to the repo’s root folder before doing a git commit and push, or you’ll see error message “fatal: this operation must be run in a work tree”.

    Push tags

  2. When a commit is known good as the one to release, most organizations tag that specific commit with semantic versioning text.

    git tag -a v1.2.3 -m\"New version\"

    -a adds the tag permanently. Without this parameter, the tag remains local.

    The v1.2.3 format is called “semantic versioning” described at http://semver.org

    PROTIP: This stores the tag within folder .git/refs/tags/.

  3. Tags require an additional git push command to be pushed to GitHub.

    git push origin --tags

    [ Whoops ]

    CAUTION: An additional push commands is needed for tags because it uses Git notes features. Since notes were not designed for many notes/tags, using them is known to slow commits.

    Delete branch

  4. Because branches are just markers within Git, once a feature branch is in GitHub, that branch can be deleted from the local repo:

    git branch -d feat1
  5. and from GitHub (by specifying that colon in front of the branch name).

    git push origin :feat1

    NOTE: The colon is the secret special sauce. There is no “delete” command with this.

7.x Pull and Merge

Unless you have been designated a committer in the upstream repository, you can’t directly push changes to it like you can with your own repo.

But you can send a request to committers of that upstream repo to pull and merge changes from your forked repo.

Each Git hosting service has their own GUI to make such requests.

GitHub calls them Pull Requests.
GitLab calls them Merge Requests.

That is why my script has “Press when ready” steps - for all three manual actions below:

1. Create a pull/merge request

  1. To make a pull/merge request, log into your Git hosting GUI and navigate to your origin repo.

  2. Click Compare. If there are differences, you’ll see a green “Create pull request”.

  3. Type in a Title for the request, such as “For the class”.

  4. Click the green “Create pull request”.

    You can make more changes.

    Alternately, there is a command to request the pull from the upstream repo. For example:

    git request-pull v1.2.3 https://github.com/hotwilson/master  master</em>
    
    • The URL must be specified (rather than a remote designator such as “upstream”).

    • This feature is described at: http://git-scm.com/docs/git-request-pull which says “this will produce a request to the upstream, summarizing the changes between the v1.0 release and your master, to pull it from your public repository.”

    See https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/

    Squash and merge

  5. Next, on a different browser, login as the committer of that upstream repo.

    If you don’t own the account, talk to the owner (class instructor).

  6. Navigate to the repo that was forked, such as hotwilson.

  7. Click the “Pull requests” tab.

  8. Check the request that you added in the step above.

  9. If there are no conflicts, click Squash and merge (or in other words, accept) the request.

    git-alt-merge-309x262-20359

  10. Click “Confirm Squash and merge”.

    A purple “Merged” appears at the top of the screen.

    Add file to upstream

    While still in the upstream repo (hotwilson), establish conditions for the next set of steps, in the upstream remote, make a change such as adding a file.

  11. Click “Create new file” button.

  12. Type in a new file name. I like a date and time such as

    2018-09-30-8

  13. Click under “Edit new file”.

  14. Type in some text, such as “hello”.

  15. Press Shift+down arrow to scroll to the bottom of the page.

  16. Click the green “Commit new file”.

8.x Update origin with upstream changes

Next, let’s look at what happens if, over time, changes occur in the upstream repo.

Based on https://help.github.com/articles/syncing-a-fork

  1. Open a different browser (Firefox or Brave) to login and make a change. In the script there is a message:

    Press any key after adding a file

  2. To enable dowload by Git, we add the upstream remote.

    git remote add upstream "https://github.com/$OTHER_ACCT/$OTHER_REPO"
    

    which by default, the variables are expanded to:

    
    git remote add upstream https://github.com/hotwilson/some-repo
    

    It’s OK to see

    fatal: remote upstream already exists.

    Otherwise, expect no output returned from this command if ok.

    [ Whoops ]

  3. To verify whether the origin remote was created, as usual, with the repo.

    git remote –v

    -v is for verification.

    Expect to see both your origin and upstream remotes listed, such as:

    origin   git@github.com:wilsonmar/some-repo.git (fetch)
    origin   git@github.com:wilsonmar/some-repo.git (push)
    upstream git://github.com/hotwilson/some-repo.git (fetch)
    upstream git://github.com/hotwilson/some-repo.git (push)
    
  4. To obtain changes locally, many would rather NOT issue a git pull upstream command, which blindly fetches and automatically merges changes. Differences in the same line within the same file, Git is forced into automatic conflict resolution mode.

    git fetch upstream master

    Example output:

    remote: Counting objects: 2, done.
    remote: Compressing objects: 100% (2/2), done.
    remote: Total 2 (delta 1), reused 0 (delta 0), pack-reused 0
    Unpacking objects: 100% (2/2), done.
    From git://github.com/hotwilson/some-repo
            * branch            master     -> FETCH_HEAD
    8843446..b33e370  master     -> upstream/master
    
  5. git checkout master

    Already on 'master'
    Your branch is behind 'upstream/master' by 1 commit, and can be fast-forwarded.
      (use "git pull" to update your local branch)
    
  6. List delta file names only

    git diff HEAD @{u} --name-only

    Sample output:

    newfile
    

    git merge upstream

  7. git merge upstream/master”

    git merge upstream/master

    Sample output:

    Updating 8843446..b33e370
    Fast-forward
     newfile | 1 -
     1 file changed, 1 deletion(-)
     delete mode 100644 newfile
    

    Notice the slash separator between the upstream remote and the upstream branch (master).

    CAUTION: Once you start a merge, nothing else can be done until you reconcile ALL conflicts. One cannot save a partially-resolved merge. There is no way yet of testing a partially merged tree. You can’t go back if you make a mistake.

    [ Whoops Ref ]

  8. git push origin master”

    git push origin master

    Sample output:

    Counting objects: 2, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (2/2), 608 bytes | 608.00 KiB/s, done.
    Total 2 (delta 1), reused 0 (delta 0)
    remote: Resolving deltas: 100% (1/1), completed with 1 local object.
    To github.com:wilsonmar/some-repo.git
    8843446..b33e370  master -> master
    
  9. To obtain changes locally, many would rather NOT issue a git pull upstream command, which blindly fetches and automatically merges changes. Differences in the same line within the same file, Git is forced into automatic conflict resolution mode.

    • “Unintentional” merge commits are what some call “evil”:
    git pull --rebase

    PROTIP: To set it up so every branch you ever create on any repository is set to pull with rebase, set this global configuration:

    git config --global pull.rebase true

    Prior to version 1.7, it was:

    git config --global branch.autosetuprebase always

9.x Update your origin repository

In this section we change something on GitHub/GitLab and then fetch it locally, see what changed, and merge it.

  1. “Change something on the origin in GitHub wilsonmar/some-repo …”

    Press any key after adding a file ...
  2. Fetch (instead of pull)

    git fetch origin master
    

    Sample response:

    remote: Counting objects: 2, done.
    remote: Compressing objects: 100% (2/2), done.
    remote: Total 2 (delta 1), reused 0 (delta 0), pack-reused 0
    Unpacking objects: 100% (2/2), done.
    From github.com:wilsonmar/some-repo
            * branch            master     -> FETCH_HEAD
    9c9468b..bbac92d  master     -> origin/master
    
  3. See what changed:

    git diff master..origin/master
    

    Sample response:

    ----------------------------------------------------------
    renamed: me 2018-09-04b to 2018-09-04-a
    ----------------------------------------------------------
    ----------------------------------------------------------
    added: smoky
    ----------------------------------------------------------
    @@ -0,0 +1 @@
    +bear
    ----------------------------------------------------------
    added: xome
    ----------------------------------------------------------
    @@ -0,0 +1 @@
    +hello
    
  4. Merge tracking branch:

    git merge origin master -m"OK" --no-edit
    

    Sample response:

    
      

GitHub API

  • See https://gist.github.com/caspyin/2288960 about GitHub API
  • From https://gist.github.com/robwierzbowski/5430952 on Windows
  • From https://gist.github.com/jerrykrinock/6618003 on Mac

BONUS topics

More configuration settings

For example, GitHub Enterprise users whitelist hostnames using:

  • git config –global –add hub.host my.example.org

Change default commit message editor program to Sublime Text (instead of vi):

git config core.editor "~/Sublime\ Text\ 3/sublime_text -w"

Allow all Git commands to use colored output, if possible:

git config color.ui auto

Get the size of what was transmitted on the current repo folder:

git count-objects -v
count: 1749
size: 12308
in-pack: 344
packs: 1
size-pack: 109
prune-packable: 0
garbage: 0
size-garbage: 0
   
git remote show origin

But I have a script that installs them and other apps on a Mac according to a specification file. It’s at https://github.com/wilsonmar/mac-setup/master/blog/mac-install-all.sh

Set default branch

DEFAULT_BRANCH="develop"
git symbolic-ref HEAD refs/heads/$DEFAULT_BRANCH
cat .git/HEAD
git branch -avv

Viewing files

  1. To see files changed vs. the tracking branch:

    git diff HEAD @{u} --name-only
    
  2. To see files changed, including uncommited local modifications vs. the tracking branch:

    git diff @{u} --name-only
    
  3. To see lines changed vs. the tracking branch:

    git diff master origin/master
    

    Compare tracking branch

  4. Before you fetch/pull, display incoming changes from remote origin master branch :

    git log ^master origin/master
    

    Nothing is returned if no changes were found.

  5. To see the difference introduced by fetch:

    gitk origin/master..master
    

    Alternately, some others prefer using 3rd-party merge utilities.

  6. To display outgoing changes before you push:

    git log master ^origin/master
    
  7. then a

    git checkout master

    [ gfu is the shortcut ]

    gitk for diff

    Now we can use a utility such as gitk to see what changes came in.

    • Click the commit listed at the top of the list, which is the most recent commit.

    • Alternately, there is also utilities vimdiff, meld, difftool, etc.

    • For Linux: https://wiki.gnome.org/Apps/Gitg/

    • To see the difference between what is in last commit vs. what’s in the working folder:

      git difftool
    • To see the difference between what is in last commit vs. what’s added in the index cache:
      git difftool --cached
    • You can make a default command such as this to explicitly specify some file to compare:
      git diff HEAD HEAD^ -- file1

    git push origin master

  8. git push to update origin master on our forked repository.

    There are variations to these commands, but this is the typical workflow.

    [ Whoops ]


    Changes in my repo

  9. In GitHub, on a repo you can change, create a new branch named “sample1”, create a new file, click the pencil to edit the file. Save it. For example, create a new file and change something.

    Even if you are not working with a repo that others update, you yourself may update files on GitHub.

fetch –dry-run

  1. Locally, when a team creates branches of the master, everything that the team is working on can be seen with one command:

    git fetch --dry-run
    

    A sample response is this from Scott Chacon:

    remote: Counting objects: 3032, done.
    remote: Compressing objects: 100% (947/947), done.
    remote: Total 2672 (delta 1993), reused 2328 (delta 1689)
    Receiving objects: 100% (2672/2672), 16.45 MiB | 1.04 MiB/s, done.
    Resolving deltas: 100% (1993/1993), completed with 213 local objects.
    From github.com:github/github
            * [new branch]      charlock-linguist -> origin/charlock-linguist
            * [new branch]      enterprise-non-config -> origin/enterprise-non-config
            * [new branch]      fi-signup  -> origin/fi-signup
    2647a42..4d6d2c2  git-http-server -> origin/git-http-server
            * [new branch]      knyle-style-commits -> origin/knyle-style-commits
    157d2b0..d33e00d  master     -> origin/master
            * [new branch]      menu-behavior-act-i -> origin/menu-behavior-act-i
    ea1c5e2..dfd315a  no-inline-js-config -> origin/no-inline-js-config
            * [new branch]      svg-tests  -> origin/svg-tests
    87bb870..9da23f3  view-modes -> origin/view-modes
            * [new branch]      wild-renaming -> origin/wild-renaming
     

    PROTIP: Branches such as “origin/wild-renaming” in the sample above is a remote tracking branch. File in your working folders are not updated by git fetch, which is the reason why we use it rather than git pull. Local branches don’t have the remote prefix and slash.

    [ Whoops ]

Pull rebase with git up alias

Use the Git utlity which resolves conflicts: It tries to find out which commits are really your local ones, and which had come from upstream in an earlier fetch.

git pull --rebase --autostash
   

As noted here, this finds the starting point for rebase by looking at the reflog of the remote tracking branch (the tips of successive git fetch operations on origin).

  • https://coderwall.com/p/7aymfa/please-oh-please-use-git-pull-rebase

--autostash (introduced in git 2.9 of June 2016) automatically performs stash commands before the pull so that pull works even on “dirty” trees. The automatic commands are git stash save before the pull, and then when done git stash pop. This added logic which solved a trap in automation via aliases such as:

git stash && git pull --rebase && git stash pop

If there’s nothing to stash, the first command will do nothing, but then stash pop will unstash some random stuff from before.

CAUTION: There still may be changes introduced which cause a conflict when the stash pops after a successful rebase. An example:

Created autostash: 094ad5c
HEAD is now at d39c25c repo1 - readme
First, rewinding head to replay your work on top of it...
Fast-forwarded master to 6b6e1d4262fd5bc8d2b974f81222003a6c67fea6.
Applying autostash resulted in conflicts.
Your changes are safe in the stash.
You can run "git stash pop" or "git stash drop" at any time.
   
  1. If you like the above approach enough to use it a lot, create a Mac or Git alias.

    On a Mac, you can type only 3 letters (gup) by defining in your ~/.bash_profile:

    alias gup='git -c rebase.autoStash=true pull --rebase'
    

    Alternately, on any system, define this command:

    git config --global alias.up '!git pull --rebase --autostash'
    

    or

    git config --global alias.up '!git fetch && git rebase --autostash FETCH_HEAD'
    

    This would enable you to type only this (reminescant of Subversion):

    git up
    

    The above alias definitions obsoletes git-up and the Python port on Windows as well as previous suggestion to define a global default (saved in the global .gitconfig file) which automatically inserts --rebase parameter onto git pull commands:

    git config branch.autosetuprebase always
    

    [ Whoops ]


Trunk-based Development in MS Release Flow

Thoughtworks advocates for their Trunk-Based Development instead of using different branches, so every commit keeps the repository production ready.

VIDEO: Git patterns and anti-patterns for successful developers [20:25] at Microsoft Build conference May 7-9, 2018 by Edward Thomson (@ethomson) about Visual Studio Team Services says Microsoft now recommends Trunk-based Development, where coding is based on the trunk, which is a synoym for the master branch. Make Small, simple changes and integrate into the master branch. easier to code review small chunks fewer merge conflicts encourages pull requests simpler to ship, faster velocity

Keeping code isolated from other developers is Technical Debt. So code in feature toggle codes which allows features to be turned on or off in production.

[8:51] GitHub Flow adds an additional step

  1. Master is locked temporarily
  2. Merge master into the branch to deploy
  3. Build and run test suite on the branch to deploy
  4. Deploy the branch to canary servers; monitor for problems
  5. Deploy the branch to production servers; monitor for problems
  6. Merge the pull request into master; unlock the master branch

[15:20] Microsoft does “Release Flow” releases to production at the end of 3-week sprints.

Like GitHub, Microsoft fixes bugs in master first so that they can’t be forgotten and create the same error in the future. The changes are cherry-picked into the release branch.

See https://aka.ms/releaseflow dated April 19, 2018

Other videos and articles

More Videos

YOUTUBE: from GitHub Education Professional Guides: Workflow Strategies has video illustrations

https://github.com/zacksiri @zacksiri

  1. Git Flow Introduction
  2. Git Flow Feature Branch and Pushing to GitHub [11:16] 6 Dec 2015
  3. Git Flow and Github Pull Request [6:53] 1 Jan 2016

git-basics software is described by this video and blog from 2013 by @KBasarab and at:

  • http://datasift.github.com/gitflow

  • https://blog.axosoft.com/gitflow/
  • https://leanpub.com/git-basics/read
  • https://www.git-tower.com/learn/git/ebook/en/desktop-gui/advanced-topics/git-basics

References

Microsoft Learning on Azure created a series of textual tutorials in 2020:

Video tutorial: Contributing to an Open Source Project on GitHub 31 Dec 2019 by Kamran Ayub (@kamranayub, kamaranicus.com)

  1. Course Overview
  2. Getting Involved in an Open Source Project
  3. Preparing to Make a Contribution
  4. Collaborating Effectively on Pull Requests
  5. Staying Updated With Social Features

https://www.youtube.com/watch?v=YwG8C0jPapE making your own custom git commands (intermediate) on @anthonywritescode

Mike Hartl (of Learn Enough series of books) offers a thoughtful set of git commands at July 2023 https://learning.oreilly.com/library/view/learn-enough-python/9780138051143/ https://github.com/mhartl/git-utils

More

This is one of a series on Git and GitHub:

  1. Git and GitHub videos

  2. Why Git? (file-based backups vs Git clone)
  3. Git Markdown text

  4. Git basics (script)
  5. Git whoops (correct mistakes)
  6. Git messages (in commits)

  7. Git command shortcuts
  8. Git custom commands

  9. Git-client based workflows

  10. Git HEAD (Commitish references)

  11. Git interactive merge (imerge)
  12. Git patch
  13. Git rebase

  14. Git utilities
  15. Git-signing

  16. Git hooks
  17. GitHub data security
  18. TFS vs GitHub

  19. GitHub actions for automation JavaScript
  20. GitHub REST API
  21. GitHub GraphQL API
  22. GitHub PowerShell API Programming
  23. GitHub GraphQL PowerShell Module