Git

Updated: October 15, 2024

Git is a distributed version control system created by Linus Torvalds.


Table of Contents


tldr

# see what files have been modified or added
git status -sb

# show edits on commits (run before git add)
git diff

# stage files for commiting
git add -A

# commit staged files for pushing to repository
git commit -a -m "summary of changes"

# push changes to remote repository
git push

# show brach and merge history with commits
git log --oneline --graph


Installation && Setup

For Graphical User Interface (GUI) need to get it from:

OFFICIAL
FORK
GITKRAKEN

Command Line Interface (CLI) || Terminal User Interface (TUI)

Debian, Ubuntu: apt install git
Arch, Manjaro: pacman -Syu git
Fedora: yum install git
Homebrew: brew install git
Chocolatey: choco install git


Setup

Git Ignore

# add file with exact name to exclude files/types listed within from git
.gitignore

Username, Email, Text Editor

Before using a git repository to host version control of your projects a user.name and user.email needs to be setup. It can also be useful to setup a default text editor.

# user settings
git config --global user.name "John Doe"
git config --global user.email "johndoe@example.com"
git config --list   # lists all settings currently set
git config --global --add user.email "secondemail@email2.com"

# git settings
git config --global --add --bool push.autoSetupRemote true
git config --global init.defaultBranch trunk

# default editor (used for --global --edit) 
git config --global core.editor "code --wait"   # vscode
git config --global core.editor "nvim"  # nvim

git config --list --show-origin  # shows where file is located
git config --global --edit    # edit file with default editor

# make sure a file or folder is not being ignored if they are not showing up
git check-ignore ~/projects/websites/www.d3c3p7.com/ --verbose -n

# set the gpg tool
git config --global gpg.program gpg         # this will work
git config --global gpg.program gpg2        # if gpg2 is installed but still doesnt work as of Feb 2023

# turn on REuse Recorded REsolution
git config --global rerere.enabled true

Gitlab

After creating or importing the key onto the machine.

# List key information, we need number in sec
gpg --list-secret-keys

# Add key to use for signing
git config --global user.signingkey 121CDB71FA9A62D8

# Make it so don't need -S to sign commits
git config --global commit.gpgsign true

Github

Generate and add your key to GitHub

# Every commit will now be signed
git config --global commit.gpgsign true

# Where ABCDEF01 is the fingerprint of the key to use 
git config --global user.signingkey ABCDEF01

# Available in git logs
git config --global alias.logs "log --show-signature"

Git Aliases

This file is used to create aliases (ways to shorten commands).
git config --global alias.co checkout   # git co
git config --global alias.br branch     # git br
git config --global alias.ci commit     # git ci
git config --global alias.st status     # git st

USEFUL
git config --global alias.unstage 'reset HEAD --'   # git unstage
git config --global alias.last 'log -1 HEAD'    # git last (see last commit easily)

to make external commands use ! at the start of the command eg:
git config --global alias.visual "!gitk"

OTHER SETTINGS
Save time typing password everytime just run this command:
git config --global credential.helper cache   # keep password in memory for a few minutes

Local » Github || Gitlab

# First create a repo on https://github.com
# mkdir & files:
git init    # start tracking project @pwd
git status
git add -A    # stages all files.
git commit -m "First Commit"
git remote add origin https://github.com/octocron/newrepo.git
git push -u origin trunk   # will ask for github creds

Github || Gitlab » Local

git clone [url]   # clones a repo to your local stage
git clone [url] [newname]   # " " with new name


Git Add

NEVER use git add *

git add                         # start tracking files
  -A, --all                     # adds all files in entire working tree (subdirs and parents)
  -p, --patch                   # choose what changes to commit
  -u, --update                  # adds only modified or deleted files.  Leaves out new (untracked) files
  -i, --interactive             # interactively add files

git add .                       # adds all files for only that directory
git add --no-all some_dir/      # adds all files for that directory except for ones being deleted

Git Branch

Switching branches changes the files in your working directory (moving forward or backward in time)

git branch              # shows all branches. * is HEAD location.
  -d, --delete          # delete a branch, must be merged first
  -D                    # if you really want to delete unmerged branch and lose the work.
  -m, --move            # renames branch, does not delete, but will fail if branch already exists
  -M                    # renames branch, deletes any branch of the same name.
  -u, --set-upstream    # set upstream branch.
  -v                    # shows last commit on each branch
  -vv                   # shows all branches and last commit on each branch

  --merged              # shows branches that have been merged, those w/o * can be deleted.
  --no-merged           # shows unmerged. these will fail to delete if you try.

  <name>                # creates a new branch on the commit you are currently working at HEAD

Git Checkout

git checkout
  -                         # switch to previous checked out branch
  trunk                     # switches over to trunk branch
  -b trunk                  # creates new branch called trunk and switches over to that branch
  --track origin/trunk      # place to push | pull
  -b trunk origin/trunk     # create new local branch && track origin/trunk remote branch
  -- <file>                 # reverts changes in your directory (copies old file back over current file)

Git Clone

Protocols: Local, HTTP, SSH, and Git
Speaking of Git protocol (listens on dedicated port 9418) uses absolutely no authentication.
For a repo to use this protocol you must create git-export-daemon-ok file.
Either the git repository is available for eveyone to clone or it isnt.
Generally there is no pushing over this protocol and it is never advised to turn on push access.
{i would only use this for a deployment node}

# clone a local repository | uses hardlinks or directly copy the files it needs.
git clone /opt/git/project.git

# file:// causes git to fire up processes for transferring data over a network;
# it's not efficient timewise but it gives you a clean copy without extraneous references.
git clone file://opt/git/project.git

# add local repo to existing git project so now you can push or pull, same as over a network.
git remote add local_proj /opt/git/project.git

# clone repo over ssh
git clone ssh://megacron@192.168.0.123:project.git

# clone repo over ssh, git knows your username and can figure out protocol
git clone @192.168.0.123:project.git


### Setting up the server

git clone --bare my_project my_project.git    # have to export an existing repo into a bare repo to set up.
# bare repository directories end in .git.  this is like cp -Rf my_project/.git my_project.git  the difference
# is it takes the git repo by itself, without a working directory, and creates a directory for it alone.
# bare repositories do not contain a working directory.

# All that is left is to put the bare repo on the server and setup its protocols:
# server called: git.example.com and you have ssh access and you want your repos in opt/git directory
scp -r my_project.git user@git.example.com:/opt/git

git init --bare --shared    # auto add group write permissions for those who have ssh access.

# all you need to collaborate is an ssh server and a bare repo.

Git Commit

git commit -m "commit message"      # commit with inline commit message
  -a, --all                       # adds files to staging are before commiting (skips staging)
  -f, --force                     # forces removal of a file during a commit
  -p, --patch                     # choose what changes to commit
  -m "message"                    # summary of changes
  -S                              # sign commit with gpg key

Git Diff

# must be run before git add
git diff                # show unstaged changes
  HEAD                  # show all uncommitted changes
  --staged              # added but not committed
  --cached              # what is staged so far
  --check               # identifies whitespace
  --summary <commit>    # Output summary since given commit

  branchA branchB [--] <file>               # compare file between two branches
  branch:<fileOther> <fileCurrent>          # compare files from other branch to current

Git Fetch

# grabs changes from remote without modifying your working directory
git fetch
  --all                # fetches all remotes
  --prune              # delete local references to remote branches that are deleted upstream
  --tags               # fetches tags

  <remote>             # sync all data to local directory

Git Log

git log                 # list all commits as --long
  --no-merges           # see commit history format
  -p                    # shows diffs of all commits
  -p <file>             # shows history of a dir || file, including diffs
  -p -2                 # shows diff in ea. commit, limited to last 2 entries
  --stat                # abbr. stats for ea commit

  --pretty=[oneline|short|full|fuller|format]    # changes log output

  [format]:"%H-hash %h-abbr hash %T-tree hash %P-parent hash %an-author name
  %ae-author email %ar-relative date %cn-committer name %s-subject"

  --oneline -3                        # for example last 3 commits
  --since=2.weeks or  [--until]       # time limiting options
  --author    or  [--committer]
  --Sfunction_name                    # shows commits where string is +/- from code
  --grep                              # show commits containing "string" in commit message
  --pretty=[oneline|format] --graph   # shows branch and merge history
  --decorate                          # see where branch pointers are pointing

  --oneline --decorate --all --graph  # graph branches, commits, tags of entire repo

Git Merge

Merging means take 1 ++ 2 ++ 3 ==> 4

# Fast Forward == merge changes between two branches with no other merges in timeline.
git mergetool         # handle merge conflict
git merge <branch>    # merge branch into current branch
  @{u}                # shorthand for origin/trunk || whatever you have set as tracking
  --abort             # if merge conflicts
  --edit trunk        # edit merge message
  --no-ff trunk       # merge trunk a with merge commit

Git Pull

# git pull = git fetch ++ git merge
git pull                          # pull changes from remote
  --rebase                        # pull changes && fast forward
  <remote> <branch>               # pull changes, merge >> HEAD

Git Push

git push                          # push committed changes to repository
  --all                           # push all branches
  -f, --force                     # removed commits from remote
  --tags                          # push tags
  -u, --set-upstream              # add upstream tracking reference

  <remote> <branch>               # push branch to remote counterpart
  <remote> --delete <branch>      # delete remote branch (removes pointer) so can be recovered
  --prune <remote>                # delete remote branches that are not local

To force push need to turn off protected mode in Settings >> Repository >> Protected Branches

Rebase

In GIT there are two main ways to integrate changes from one branch to another: merge || rebase
Rebase: take all the changes that were commited on one branch and replay them on one another.
Does not make 3 way merge and create a merge (4th commit). It goes back to the common ancestor
of the two and the one checked out (feature) overlays the one being rebased (usually the trunk).

git rebase
  -i, --interactive <target>        # can be a branch or hash
  --continue                        # continue rebase
  --skip                            # skip current commit
  --abort                           # abort rebase
  -X theirs <branch>                # Auto-resolve conflicts favoring current working branch


# place feature branch changes onto trunk
git checkout feature; git rebase trunk

# you could checkout trunk branch and do a fast-forward merge.
git rebase --onto <new> <from_old>
# now you can fast-forward trunk branch:
git checkout trunk; git merge feature

# you can skip checkout on a rebase by:
git rebase trunk feature; git checkout trunk; git merge <remote>

# then remove the old branches:
git branch -d <old>; git branch -d server

WHEN TO NEVER USE REBASE:
commits that exist outside your repository, never rebase anything you have pushed somewhere.
rebasing means abandoning existing commits and creating new ones that are similar but different.
basically only rebase unshared local changes before you push.

Git Refspec

A refspec is done when two topics (branches) are merged. Push merged work of one branch to the one on the server.

git push -u origin featureB:feature Bee
    -u    # this is short for --set-upstream, which configures the branches for easier push/pull later.

Git Remote

Creating a shortname (remote) allows use in place of url.

git remote                            # list configured remotes
  -v                                  # show the urls

  add <shortname> <url>               # add new remote !!important, link local machine to existing repo gitlab or github
  get-url <remote>                    # show url
  rename <current> <new>              # this adjusts branches: new/trunk
  remove <remote>                     # removes a remote
  set-url <remote> <url>              # change url
  show <remote>                       # show remote details

set-url origin git@github.com:USERNAME/REPOSITORY.git    # HTTPS -> SSH
set-url origin https://github.com/USERNAME/REPOSITORY.git    #SSH -> HTTPS

Git Reset

git reset                           # unstage everything
  HEAD [file]                       # unstage a file that was added
  HEAD~                             # undo last commit while keeping changes in filesystem
  --hard HEAD~1                     # remove commit locally (deletes changes 1 commit back)
  --soft HEAD~3                     # remove commit and place files unstaged (3 commits back)

Git Restore

git restore                         # drop tracking
  --source                          # extract files as they were in another commit

Git Revert

git revert                          # rollback a commit?
  HEAD                              # revert most recent commit
  HEAD~4                            # revert 5th last commit
  8oo8135                           # revert to specific commit

Git Stash

Stashes carry over from branch to branch. If you have modified files that you do not want to commit while in the wrong
branch, then create a stash. Now you can move to another branch. (you can’t change branches if the branch isn’t clean.)

git stash 
  save "worked on add function"   # makes a save point of your work on a file without commiting it.
  list                            # where stashes can be retrieved.
  show -p                         # shows changes in stash -p shows diff
  apply stash@{0}                 # puts stashed changes back into files.  stash remains.
  pop                             # applies changes in stash@{0} and deletes the stash from list.
  drop stash@{0}                  # deletes the stash without applying any of its work.
  clear                           # deletes ALL stashes.

Git Status

git status                  # show changed files not added
  -b, --branch              # shows status of files (un/modified, un/tracked un/commited)
  --show-stash              # show number of entries
  -s, --short               # short format for branch and tracking

# Letter meanings in short mode
  = unmodified
A = added
C = copied
D = deleted
M = modified
R = renamed
T = file type changed (regular, symbolic link, module)
U = updated but unmerged

Git Tags

# ANNOTATED - stored as a full object in git database
git tag -a v1.1 -m 'my version 1.1'   # creates an annotated tag

# LIGHTWEIGHT - like a branch that doesnt change -- points to a specific commit
git tag v1.1

git show v1.1   # shows tag data along with the commit that was tagged

# You can tag commits even after you pass them, just look at commit history:
git log --pretty=oneline    # shows commits with checksums
# then,
git tag -a v1.2 9fceb02

# push doesnt transfer tags to remote servers, they have to be pushed explicitly. eg:
git push origin v1.1

if many tags have been created you can push many of them at once:
git push origin --tags

Git Worktree

Work on repo in another path without re-cloning.

# copy files from trunk to /temp creating new branch hotfix
git worktree add -b hotfix /temp


How To:

Rename A Branch

# on the branch to rename
git branch -m <new>

# on a different branch from one being renamed
git branch -m <old> <new>
git push origin :<old> <new>    # : means delete

# reset the upstream branch
git push origin -u <new>

Scrap everything to previous commit

For when commits buildup into a giant mess of broken things.
With 2 commands we can jump back to last working commit.
removing all commits above. May need to allow force push at GL/GH

# lets look at last 20 commits
git log --all --oneline -n 20

# reset head to previous commit typically 7 digits
git reset --hard d7d9fg6

# if we like the state, we need to force push, this drops all commits above
git push origin trunk --force

Clone Another Project Into A Branch

# Make a new branch
git checkout -b strangerDanger

# add repo of other project as a remote
git remote add <shortname> https://github.com/username/other-project.git

# grab data of other project
git fetch <shortname>

# merge
git merge <shortname>/main

# place new branch on remote
git push origin/main

Correct Commits && Affect History

# NOTE:  This impacts history and hash.  Do not use --amend for commits that have been pushed.
git commit --amend -m "Redo commit message"   # works best immediately after a commit.
git commit --amend    # then hit enter | this will amend any new staged files to previous commit.

Correct Commits =/= Affect History

git revert 12345678   # undo the changed in that commit.

Copy Commit to Another Branch

# NOTE: use [git log] to get |hash value| of commit you want to move from one branch to another.
                   # |1st 7 digits or so is enough| from commit@ incorrect branch
git checkout correctbranch
git cherry-pick 12345678

Copy Deleted File From Earlier Commit

# find commit that deleted a file
git log --diff-filter=D --summary

# list all commits for that file (--follow helps with renames)
git log --follow -- path/to/file

# list last 5 commits to get hash
git log --oneline -5

# grab deleted file into Head (~1 would be previous commit)
git checkout 838de83~1 path/to/file

Delete Commit From Branch That Has Been Moved To Another Branch

git checkout branch # where commit is to be deleted.
git log                       # to see commits
git reset --soft 12345678     # removes commit and puts files back to staging area.  Keeps modifications.
git reset 12345678            # --mixed (default) | puts files to working directory.  Tracked and untracked.
git reset --hard 12345678     # removes changes in files that were tracked but leaves untracked files alone.
git clean -df                 # will remove any untracked directories or files.

Recover Files Deleted Within 30 Days From A Reset

git reflog                    # shows commits in the order of when you last referenced them.
git checkout 12345678         # checkout the hash of the commit that is before reset commit.
# this is a detached head state and will be trashed in the future.  It is not a branch.
git log                       # can see the commits in that state again
git branch backup             # this will save the changes from the head state into a branch.

Be A Good Commitor