Just because I forget (g)it

It has been a while that I have been working with GIT, mostly on github and I noticed I kept returning to this one note in a text file that used as an external memory when ever I needed to clone a new Github repo and start working on it. So about time to transform this into a sharable blog post. Especially working on a windows system can be tricky to get it working how it should. (for some reason some eployers keep insisting me to work on windows ;-) )

I will dicuss the following things here:

  • What is Git
  • Setup Git for Github
    • Additional tools
    • Generation of SSH keys
  • Basic operations and commands
  • Best practises
    • Ammend your PRs
    • If all else fails …Fuckit!
  • The Better Best practises
    • Branch you work
  • Working with branches
  • For the Git Pros!

What is Git

straight from Wikipeadia:

Git is a distributed version-control system for tracking changes in source code during software development. It is designed for coordinating work among programmers, but it can be used to track changes in any set of files. Its goals include speed, data integrity, and support for distributed, non-linear workflows.

For me since I have been tracking, storing ans sharing several project on Github it is the de-facto standard to sync and keep versions for my projects.

Setup Git for Github

I install the following:

  • Chocolatey - a Windows package manager
  • git.install - Git for Windows
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Force                     
iwr https://chocolatey.org/install.ps1 -UseBasicParsing | iex
choco install git.install -y

Aditional tools

Just to improve your working experience a little I also install another Console Emulator which work nicely with multiple tabs etcetera. And to finish it of I use some customisation to get my

I install the following:

  • ConEmu - Terminal Emulator for Windows
  • posh-git - PowerShell functions for working with Git
choco install conemu -y
Install-PackageProvider NuGet -MinimumVersion '2.8.5.201' -Force
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module -Name 'posh-git'

For detail on how to customise your prompt proper read this https://hodgkins.io/ultimate-powershell-prompt-and-git-setup.

Generation of SSH keys

[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\Git\usr\bin", "Machine")
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\Git\bin", "Machine")
mkdir $env:USERPROFILE\.ssh
ssh-keygen -t rsa-sha2-512 -b 4096 -C "[email protected]" -f $env:USERPROFILE\.ssh\id_rsa
Get-Content $env:USERPROFILE\.ssh\id_rsa.pub | clip

If you have executed the above you now have your key ready to insert into Github in your clipboard. Open up your GitHub settings and choose ‘SSH and GPG’ keys on the left.

Now you can start using the Key from you some usefull comands:

Get-Service -Name ssh-agent | Set-Service -StartupType Manual
Start-Service ssh-agent
Add-SshKey $env:USERPROFILE\.ssh\id_rsa
# List ssh keys
ssh-add -l
# Remove all ssh keys
ssh-add -D
# Test ssh connection to GitHub
ssh -T [email protected]

You might want to avoid a chatty ssh login (banners, disclaimers etc.) on a server like this:

New-Item -Path $env:USERPROFILE\.ssh\ -Name "configs" -ItemType "file" -Value "Host git.host_name.tld`r`nLogLevel QUIET"

Basic operations and commands

Initial Setup

Edit the global Global Configuration file

git config --global user.name "Firstname Lastname" 
git config --global user.email [email protected]
git config --global credential.helper cache 
git config --global push.default matching

or use <git config –global -e> and edit the file manualy

To donwload a repo and make the settings correct

git clone [email protected]:user_name/repo_name.git
cd repo_name
git remote add upstream [email protected]:original_repo/repo_name.git
git remote -v

Rebase before making changes

When useful: You need to update your clone/fork BEFORE making changes, or AFTER committing changes to master to make sure you are working in the latest version.

# Grabs the latest changes from the upstream master
git fetch upstream master
# Puts your changed work on top of the previously fetched work
git rebase upstream/master
# Force pushes the change to your own GitHub repo
git push

( Or Fuckit if you feel lazy )

Best practises

Amend your PRs

Commit an additional change (before commit is merged) – only works if you want to add all files in your local repos When useful: You have just sent a pull request, but want to commit an additional pull request (in case you forgot something, or the merger points out another detail or error).

# Add all changed files
git add -A
# Check what you added if needed
git status
# Commit the changes to existing commit, don't change commit name.
git commit --amend --no-edit
# Force push to your repo
git push -f

Keep in mind that the default behaviour when amending is that you actualy add it also to any Pull Request that you amend the change to. This is usefull of course when ever there is a 4-eyes review with some minor changes requested from the reviewer. Just amend and the chnages are squashed into the already existing PR.

If all else fails …Fuckit!

You can create aliasses with the following commands

git config --global alias.hist "log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short"
git config --global alias.fuckit ‘!git fetch upstream && git checkout master && git reset --hard upstream/master && git push origin master’. 

These are just examples but I do often start with working on a repo bu hard flushing any diffs between my for fork & localmaster and the original upstream. Just use and you are ready to start working from hete on.

The Better Best practises

The above “Best practises” work fine especially when working with small teams and low level of commits. But especially when working with a large team and high frequency of changes especialy on the same files. Things can get tricky and a git push -f can make things worse and create confilict for you fellow engeneers or developers. Because you actually can force push / flush some one else his or her changes.

Instead use --force-with-lease if you have to. It is hardly a perfect solution, but it is preferable to its blind, wrecking ball cousin. Using git push --force-with-lease will check that the remote branch is in the state that we expect before pushing.

Create some aliases if you like to prevent you having to type such a long command often

git config --global alias.fpush ‘push --force-with-lease’
git config --global alias.fp ‘push --force-with-lease’
git config --global alias.pf ‘push --force-with-lease’

Branch you work

Using branches has 2 big advantages. If you work proper with branches you can avoid having to forced pushing your code. The other advantage is of course that you can work on seperate issues and features at the same time and also have clean working master branch where you can fetch the latest chnages form the upstream into. This way you can always start to work clean on the next issue without interferance with a ongoing improvement cycle that otherwise first needs to be finished.

# checkout and create a branch
git checkout -b story_4134
# Same as 
git branch story_4134 | git checkout story_4134
# You can work on the branch
git commit -a -m '[story_4134_1] Create new thing your working on'

Now comes the interesting part you get interupted and need to work on a hotfix:

# checkout back to master
git checkout master
# collect latest change the issue might just be in something just changed
git fetch upstream master
# start wokring on your  hotfix again in a branch
git checkout -b hotfix_20200309_1
# You found the issue updated files now commit it and push
git commit -a -m '[HF_20200309_1] Fix a broken thing'
git push
# now move back to the master and merge the hotfix and remove the branch
git checkout master
git merge hotfix_20200309_1
git branch -d hotfix_20200309_1

Form here you can continue to work on you improvement:

git checkout story_4134
git commit -a -m '[story_4134_2] Create new thing your working on '

For the Git PROs

Resolving Conflicts

Copy paste code to desktop as backup, otherwise code is lost after resetting.

# Fetch the latest changes from the upstream master 
git fetch upstream master
# Hard reset
git reset --hard upstream/master
# Force push to your repo
git push --force-with-lease

it can happen that you have some files in your clone, but in the master they’re gone which results in commits that won’t disappear. What you can do is stash the commits and clear them. (only use this when there is no other solution) Do these steps to get them removed

git add . 
git stash 
git stash clear
git pull -r upstream master
git push --force-with-lease

Polish your CLI

Like mentioned before a good alternate resource: https://hodgkins.io/ultimate-powershell-prompt-and-git-setup.

# Creates profile if doesn't exist then edits it
if (!(Test-Path -Path $PROFILE)){ New-Item -Path $PROFILE -ItemType File } ; ise $PROFILE

I like using the following config :

# Import module from previous step
Import-Module -Name posh-git

function Test-Administrator {
    $user = [Security.Principal.WindowsIdentity]::GetCurrent();
    (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}

function prompt {
    $realLASTEXITCODE = $LASTEXITCODE

    if (Test-Administrator) {  # Use different username if elevated
        Write-Host "(Elevated) " -NoNewline -ForegroundColor White
    }

    Write-Host "$ENV:USERNAME@" -NoNewline -ForegroundColor DarkYellow
    Write-Host "$ENV:COMPUTERNAME" -NoNewline -ForegroundColor Magenta

    if ($s -ne $null) {  # color for PSSessions
        Write-Host " (`$s: " -NoNewline -ForegroundColor DarkGray
        Write-Host "$($s.Name)" -NoNewline -ForegroundColor Yellow
        Write-Host ") " -NoNewline -ForegroundColor DarkGray
    }

    Write-Host " : " -NoNewline -ForegroundColor DarkGray
    Write-Host $($(Get-Location) -replace ($env:USERPROFILE).Replace('\','\\'), "~") -NoNewline -ForegroundColor Blue
    Write-Host " : " -NoNewline -ForegroundColor DarkGray
    Write-Host (Get-Date -Format G) -NoNewline -ForegroundColor DarkMagenta
    Write-Host " : " -NoNewline -ForegroundColor DarkGray

    $global:LASTEXITCODE = $realLASTEXITCODE

    Write-VcsStatus

    Write-Host ""

    return "> "
}
comments powered by Disqus