2019-05-8
git clone https://github.com/portalgun/hellogitworld
git clone https://github.com/portalgun/mergeConflict
Most of learning git is just figuring out what you can do
Many things difficult or impossible in GUI
GUI not always available on compute servers
Some things are easier with a client
#+ATTRREVEAL: :frag (appear)
#+ATTRREVEAL: :frag (appear)
git branch <branch-name>
git checkout -b <branch-name>
Some things you maybe didn't know…
line 1 - title
line 2 - blank
line 3 - longer description
OR
#+beginsrc bash -i
git commit -m "title" -m "description"
git add -i <larger>
git add -p <smaller>
Commit A Single File With Staged Files
git commit <file>
git reset <file>
Small demo…
Note that these will not affect a push; reset applies to just 1 branch.
I forgot to test my last commit:
git reset --soft
I need to add/fix something to my last commit:
git reset --mixed
My last commit is utter garbage and I want to trash it completely:
git reset --hard
Note that there is a difference between
git reset --soft
and
git reset --soft HEAD~1
Without specifying a commit (latter), the reset only applies to your index and workspace.
To undo a commit, you need to explicitly specify which commit you are referring to.
git reset HEAD@{n}
replace n for how many times you did the git reset command
If I did 'git reset HEAD~1' or 'git reset HEAD~20', then n is 1
If you did 'git reset HEAD~1; git reset HEAD~1', then n is 2
git stash save [<message>]
git stash list
git stash show
git stash apply stash@{n} #n is a number
git stash drop stash@{n}
git stash -u
git stash pop stash@{n} #drop and apply
git revert <ref>
Like reset, but appropriate for your origin
It doesn't undo the commit, but creates a new commit that is identical to the commit you want to 'reset to'.
How would we undo two commits, but retain changes
git reset --soft HEAD~2
This also works on individual files
#+ATTRREVEAL: :frag (appear)
Other notation - next branch point:
HEAD^
git checkout -b cleanup
echo "\nForked by Dave" >> README.txt
git add -u README.txt
git stash save Dave
#MAKE SURE THINGS ARE GOOD
git status
cat README.txt
echo "\nCreated by Matthew McCullough" >> README.txt
git commit -a -m "Signed Matthew McCullough" -m "What a great dude"
git reset --mixed HEAD~1 #HEAD~1 must be included
git stash save -u Matt #-u must come right after save
git stash list
git stash apply stash@{1}
git stash drop stash@{1}
git add -u README.txt
git commit -m "Added name Dave"
git stash pop stash@{0}
#This last line will create a conflict, which we will talk about later
#Fix the conflict by removing lines with:
#<<<<<<<
#=======
#>>>>>>>
#You can do this manually or with a stream editor:
sed -i -r '/^<{3,}/d; /^>{3,}/d; /^={3,}/d' README.txt
cat README.txt
git commit -a -m "Signed Matthew McCullough" -m "What a great dude!"
git log --graph --oneline --decorate --all
Git was meant to be scripted/aliased
#$HOME/.bashrc in linux
#$HOME/.bash_profile in mac
alias glg="git log --graph --oneline --decorate --all"
git log <tag, hash, branch,remote>
git log RELEASE_1.1
git log 8d2636d
git log origin/master
git log origin/feature_subtraction_polished
git log -3
git log --since="5 years ago"
git log --until="8 years ago"
git log --author="Peter"
git log -L <startrange>,<endrange>:<file>
git log -L 1,2:pom.xml
git log -L 1,3:pom.xml
git log -L :funcname:file
git log -L :repositories:build.gradle
git log -L :repositories:build.gradle -L :dependencies:build.gradle
git log -S''
git log -S'maven'
git log -G''
git log -G'dep.+'
git log --grep'Search messages'
git log --grep='script'
Combine as many options as you want
Shell tools
git log | less
git log | grep -ei "some string"
Gitk, Gitkraken, Magit, etc..
How to move around.
Try this:
git checkout master
git status
Try this:
git checkout RELEASE_1.0
git status
Detatched - not on newest commit
Any commits without explicitly branching will not be attached to the tree
Create a new branch off the checked out branch if you are making a commit to an older commit
grep -Ri "sum"
git log -L :sum:src/Sum.groovy
git checkout 333fd9
git checkout HEAD^
git checkout -b "testbranch"
touch TRASH
git add TRASH
git commit -a -m "created trash"
git checkout master
git log testbranch
People don't like branching because it means merging
However:
#+ATTRREVEAL: :frag (appear)
Simply, branches allow for
Think of commits as a sections in a textbook.
A branch is going to be chapters in your textbook
You can work on multiple chapters of a book at a time.
#+ATTRREVEAL: :frag (appear)
1 feature per branch, rule of thumb
Switch your usual large commits, with a merged branch.
Actually will make things easier to merge
Master & origin/master are different branches
Feature1 & feature1/master are different branches
Push/pull is just fetch + merge
Same thing in practice, but different vantage point
If you are not a manager you would need to do a pull request
#+ATTRREVEAL: :frag (appear)
How to pull request
Send me a pull request to merge "cleanup" with master
Merging is always towards the head
Think of master as a giant git beast
#+ATTRREVEAL: :frag (appear)
Default:
Why set your default to no-ff? keeps things separated by feature/user
git config --global --add merge.ff false
git config --add merge.ff false
Only merge if nothing new at merge point
git merge ff-only
cp ../hellogitworld ../hellgitworld_Backup #linux
cp -a ../hellogitworld ../hellgitworld_Backup #mac
git checkout master
git merge cleanup --no-ff
git reset --hard HEAD~1
git merge --ff-only
Rebase is ugly
Few cases when you should use it
Makes things easier, eliminates many merge issues as you go along
git config --global pull.rebase true
When you push, it will look like…
You do NOT need your repo to look exactly like remote
Nor should you try to do this, or expect it.
What matters is that both are clear
In a merge it doesn't matter where code came from
Small differences in master, larger differences between your branches
Pull master into testbranch
checkout testbranch
git pull –rebase master
git branch -D <branch>
git checkout -b <branch> <sha>
git branch -D testbranch
git checkout -b testbranch <sha>
git tag <tag-name>
git push --follow-tags
git push <remote> <tag-name> --force #if moved
git tag -a <tag-name> -m <message>
git checkout <tag-name>
git tag -d <tag-name> #local
git push --delete origin <tag-name> #remote
When a ref is created, it needs to be push - this includes new branches and tags
Everything point to point (one branch at a time), unless required or specified
eg. commits have dependencies
Explicitly need to push:
Branch example
git notes add -m "message"
git log -p notes/commits
git push <remote> refs/notes/*
git fetch origin refs/notes/*:refs/notes/*
git notes --help
git checkout 435ffce
git tag RELEASE_1.1.1
git checkout RELEASE_1.1
git notes add -m "Missing log files"
Industry standard:
Everything gets pulled top to bottom
#+ATTRREVEAL: :frag (appear)
New project/single individual/small project
#+ATTRREVEAL: :frag (appear)
Why make MASTER stable only? Default
Fetch, then diff
#commit or stash then...
git fetch
git log ..<branch> #list changes that will be merged
git diff ..<branch>
git difftool ..<branch>
difference between a & b
a..b
..b
difference between common ancestor and b
a...b
...b
everything that is in b but not in a
a..b
only in one or other
a...b
Many different diff/merge conflict tools
Many graphical git wrappers have their own
git config --global merge.tool <merge-tool>
git config --global diff.tool <merge-tool>
#git config --global diff.external <merge-tool>
git difftool <COMMIT_HASH> file_name
git difftool <BRANCH_NAME> file_name
git difftool <COMMIT_HASH_1> <COMMIT_HASH_2> file_name
git difftool RELEASE_1.1 RELEASE_1.0
git difftool RELEASE_1.1 RELEASE_1.0 runme.sh
git blame <file>
git blame -e <file>
git blame -L <start>,<end><file>
git blame -L :function <file>
git blame -L ... -M <file>
Who do I talk to, yell at?
Merges should often be a group effort
get the email of whoever added line three to runme.sh
git blame -e -L 3,3 runme.sh
git cherry-pick (commits in order to append, first to last)
git checkout Release_1.1
git checkout -b hotfix
git cherry pick <SHA> <SHA>
git tag Release_1.1h1
Complete failure to merge - updated index, not pushed
git stash
git reset etc..
merge -s <strategy>
merge -s <strategy> --strategy-option <option>
Don't confuse with -s
git log --merge
git diff --ours
git diff --theirs
git diff --base
git show :1:<filename> #common ancestor
git show :2:<filename> #your versions
git show :3:<filename> #their version
git clean -f #remove any extra files used for merge resolution
These can be piped.
#+ATTRREVEAL: :frag (appear)
checkout --theirs <filename>
git merge --abort #RESET
git checkout -m -- <filename> #START MERGE CONFLICT OVER FOR A FILE
git checkout -m -- .* #for everything
conflictstyle diff3 (shows common ancestor too)
<<<
Towards you, you are always first (left)
|||
COMMON ANCESTOR
========
THEIRS
>>>
#+beginsrc bash -i
git config –global merge.conflictstyle diff3
git checkout –conflict=diff3 <file>
Handle merge conflicts systematically with a merge tool (see diff tools from before)
git mergetool <file1> <file2>
git mergetool <branch1> <branch2>
git mergetool <sha> <sha> <file>
#...
git clean -n #check what clean will do
git clean -f #clean
git branch mergeTests
git config --global merge.conflictstyle diff3
git diff testMerge1 testMerge2 testMerge3
git difftool testMerge1 testMerge2
git checkout testMerge2
git merge testMerge1
git diff --ours
git diff --theirs
echo "this" >> test.h
git checkout -m -- .*
git merge --abort
get checkout testMerge1
git merge testMerge2
git show :2:test.h > testOURS.h
git show :3:test.h > testTHEIRS.h
git merge --abort
git merge -s theirs
git clean -n
git clean -f
Set back to normal:
git config --global merge.conflictstyle merge
Record how you applied conflict resolutions before and automatically apply them
Simply enable:
git config --global rerere.enabled true
git config --global rerere.autoupdate true
Handle merge conflicts the same way as you did before
Recorded preimage for ...
Recorded resolution for ...
Resolved ... using previous resolution.
git config –list
git config --list
git config --global \
(shows common ancestor too) \
merge.conflictstyle diff3 merge.ff false \
<meld,p4merge,ediff(emacs,smerge-mode)> \
merge.tool diff.tool <...> \
\
core.fileMode=false \
pull.rebase true \
rerere.enabled true rerere.autoupdate true
Other than a clean workflow, proper conventions and communication will solve most potential conflicts
…
Regression debugger's best friend
git checkout <commit that you know has regression>
git bisect start
git bisect bad
git bisect good <commit that you know doesn't have regression>
Git will automatically checkout a commit and expect you to label it as good:
git bisect good
or bad
git bisect bad
Git will automatically checkout another commit.
This process continues until it reaches the point where the regression was found
Automation can be done with
git bisect run ./somescript
When you are done:
git bisect reset
pastebin
sync/backup your workspace with dropbox/nextcloud
…of some things in future advanced class
Terrible idea to track large binary files (can't open in a text editor) in the default way
git-lfs track <*.filetype>
git add <largefile.filetype>
If you push your lfs to a remote and somebody clones your repo:
The data files will NOT be there.
They would need to get the files from your local machine.
You need to set:
git config lfs.url = "https://my_local_machine.com/foo/bar/info/lfs"
And this would also mean setting up your local machine to point to the www in a secure manner.
With this in mind, its wise to have a lab server that can
If this is the case, you do not need to set the above configuration.
.git/hooks
exmples located there
git config --global init.templateDir <directory>
git init
Subtrees - If you don't plan on editing dependencies
Very straightforward to use, not very flexible
Submodules - If you don't plan on editing dependencies
Very flexible, requires very careful usage