Git Crashcourse

Posted on September 2, 2022
Tags: javascript

ohshitgit because messing up is too ez

WARNING: The terms I use are nonstandard from most other tutorials.

1 Setup Credentials

Setting email and username for local repo or global env

git config user.email "example@example.com"
git config user.name "example"

git config --global user.email "example@example.com"
git config --global user.name "example"

1.1 github remote credential

If you omit the userJY:PACTOKEN part you will get an error remote: Invalid username or password. fatal: Authentication failed for 'https://github.com/userJY/logicalverification2021.git/'

git remote add origin https://userJY:PACTOKEN@github.com/userJY/blehrepo.git

1.1.1 Saving remote github Credential on local repo

git config credential.helper store
git push -u origin main:main

By pushing to remote repo you will be asked to enter username and password( password is really the auth token you have to generate in github settings).
The above command makes it so the setting is saved.

1.2 Your .gitconfig data

git config --list

delete .gitconfig to reset your config

1.3 Remote Error

remote: Permission to somebdy/bleh.git denied to someGuy.
fatal: unable to access 'https://github.com/somebdy/bleh.git/': The requested URL returned error: 403

1.4 On git push and pull

git push -u origin <SrcLocalBranch>:<TargetRemoteBranch>
git pull origin <SrcRemoteBranch>:<TargetLocalBranch>

2 The process

git add .
git commit -m "some message"

updating to remote repo like github

git push origin main

2.1 Pushing updates to Team repo

git checkout -b Fixedbugbranch
git add .
git commit -m "fixed bugs"
git push origin Fixedbugbranch

You see a push but on the team’s side, they see a pull request(PR).
PR is ran through github actions(CI system) that runs tests.
If accepted, they merge your PR branch with the main branch.

2.2 More depth, Concept

Three layers to know: commit-references(ref), branch-alias(alias), HEAD

  • layer 1 - ref : static nodes of the commit tree.
  • layer 2 - alias : static names that point to the refs
    • alias = name of branch, default branch is “main”
  • layer 3 - HEAD : dynamic pointer that can point to alias or refs

Example git tree:

                              alias(“somebranch”)
                              🠫
                             ref(branch_new)
                              🡑
ref(commit_new) ← ref(commit_old) ← ref(commit_oldest)
🠩
alias(“main”)

HEAD

Description:
HEAD(layer 1) points to branch alias(layer 2) which points to
commit ref(layer 3).
Aliases(layer 2) always points to the local head(layer 1).

2.3 Creating new Aliases = Creating new branches

Old commits(layer 1) aka non-local-head commits do not have aliases(layer 2) pointing to them but if you do make an alias, they will become a local-head(layer 1) by branching.

Do not confuse the “HEAD” pointer with the “local head of a branch”.
The “local head of a branch” refers to the local head of commit-refs at layer 1.
The “HEAD” pointer refers to the layer 3 pointer.

name command
ref (layer 1) git commit - adds a new ref node. Moves HEAD to alias of new node
alias (layer 2) git branch - assign an alias to ref node on new branch. Moves HEAD to new alias
HEAD (layer 3) git checkout - Moves head pointer to either alias or ref

Every git node has a ref that looks like 3bed42g14584b3c0c04fdcc4f503346f92be1003

We can point HEAD(layer 3) to aliases(layer 2) like “main” with git checkout main

Switching branch = move HEAD(layer 3) to alias(layer 2) of new branch git checkout someotherbranch

git checkout can
1) switch branch
2) go to older version

Pointing HEAD(layer 3) directly to ref(layer 1), Detached HEAD mode We can point HEAD(layer 3) to ref(layer 1) like “3bed42g1…” with git checkout “3bed42g1…”

If you modify files, then commit. Any change will be discarded if you switch back to main (git checkout main)

To make changes permanent, you have to build a branch by making an alias(layer 2) with your commit(layer 1). git branch SomeName

Why and When do we point HEAD to naked refs The newest version has compatibility issues and you want to build off an old version.

transverse git commit tree using the HEAD pointer. HEAD pointer can point to alias like “main” or refs like “3bed42g…”

“git branch” vs “git checkout -b”
git branch makes a new alias and branch, BUT HEAD stays on current branch
git checkout -b behaves the same as git branch BUT moves HEAD to new branch

3 Remove unstaged changes

git restore .

4 Extras

tags are just renaming refs, they are NOT at the same level as aliases. Meaning if you point HEAD to a tag, you are in a detached head state.

In gitk yellow node HEAD pointer points to blue untagged node(meaning it is not the local head of a branch)git


5 Merging

5.1 Example

Example: clone git project first commit

commit 0382315a65a3fa24595718b3bf141f09a49b6469 (HEAD -> main, origin/main, origin/HEAD)
Author: JY <example@example.com>
Date:   Mon Sep 27 16:04:23 2021 -0700

    Initial commit

0382315a65a3fa24595718b3bf141f09a49b6469 is the commit on both server and local repo.

  • Modify README.md locally (write “hello” to README.md)
$ git log
commit 070f18087be629ba7387a9242af03f85ac656910 (HEAD -> main)
Author: jy <example@example.com>
Date:   Mon Sep 27 16:15:11 2021 -0700

    update

commit 0382315a65a3fa24595718b3bf141f09a49b6469 (origin/main, origin/HEAD)
Author: jy <example@example.com>
Date:   Mon Sep 27 16:04:23 2021 -0700

    Initial commit
  • Modify README.md on github website. (write “goodbye” to README.md)

remote new head: 3295ca935359bc5fc4e8592c9057d3c3c5cd7a6d

  • Notice Remote and Local repo are out of sync now
    • Local repo head: 070f18087be629ba7387a9242af03f85ac656910
    • Remote repo head: 3295ca935359bc5fc4e8592c9057d3c3c5cd7a6d
    • Point of divergence: 0382315a65a3fa24595718b3bf141f09a49b6469

Let’s trying pushing local 070f1 changes to remote anyway

$ git push -u origin main:main
To https://github.com/userJY/gittmp.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/userJY/gittmp.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

It failed as expected
Let’s follow instructions and use git pull

$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 641 bytes | 91.00 KiB/s, done.
From https://github.com/userJY/gittmp
   0382315..3295ca9  main       -> origin/main
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

The above line shows local..remote conflict.
0382315..3295ca9 main -> origin/main

on our console we see we have been automatically put into MERGE mode.

User@WindowsPC MINGW64 ~/Desktop/gittmp (main|MERGING)

If you enter your README.md you will find the below content while in MERGE mode.

Pre-Merge state:

<<<<<< HEAD
hello


Hello 
=======
goodbye

Goodbye 
>>>>>>> 3295ca935359bc5fc4e8592c9057d3c3c5cd7a6d

Aside: You can even commit this pre-merge state.
This will update local README.md to the above content.
You can even push this new pre-merge state commit to remote github. But clearly you shouldn’t do this as it defeats the entire point of merging!!

Use abort to leave MERGE mode to return to local repo

git merge --abort

5.2 To fix up the MERGE conflicts

Go back to MERGE mode

git pull #back to merge mode
git mergetool #will ask to start default vimdiff

5.3 Non-conflict automerge

image if local was

hello

and remote was


yello

Git pull, would merge by

$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 639 bytes | 91.00 KiB/s, done.
From https://github.com/userJY/gittmp
   8401a16..d93f27c  main       -> origin/main
Auto-merging README.md
Merge made by the 'recursive' strategy.
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

5.4 Undo add

  • this is before committing
git reset --hard
  • --hard means remove modified uncommitted files
  • --soft means keep modified uncommitted files

WARNING: Calling git reset --hard AFTER git add . but BEFORE git commit ... will DELETE YOUR WORK.

5.4.1 In case of Accidental Deleted Work

In case you did do git reset --hard AFTER git add . but BEFORE git commit ... you can recover files as blobs without file names.

git fsck --lost-found
#script that recovers files and filters file by content like `bayesian net`
cd ./.git/lost-found/other
for FILE in *; do git show $FILE | grep "bayesian net" ; echo $FILE; done
git show a73ff6d1c2bbc01fc2c182ed0c07961969f27c82 > recovered.ipynb
cp recovered.ipnyb ~/recoveredfolder

5.5 Merging two unrelated histories

Example.
You made a github repo with a license.
Then you made a local repo with some web server and used git init.
Now you want to push your local repo on that “empty” remote repo.

Not so simple, we first have to merge it but it will give us an error on a naive merge since your local and remote repo have no common ancestor

git pull origin main --allow-unrelated-histories 

5.6 Removing untracked changes

git clean -f

You might use this when you did a merge but Ctrl-C or exited out in the middle so now you’re left with temp MERGE files.

6 Git Scenarios

6.1 Creating your git project

Say we spent time building a project locally called A.
We turn it into a git repo after it is finished.
How do we push it onto github?
First we need to create an empty github repo.
But now we have 2 repos with separate histories.

{local}                     {github}
mkdir A                     create github repo B
git init A              
git remote add origin B

3 types of pulls:
1) git pull origin --no-rebase main
  [Nothing happens]fatal: refusing to merge unrelated histories 
2) git pull origin --rebase main
  [Success]This puts your remote commit B as first then local commit as recent.
  B --> A
2) git pull origin --ff-only main
  [Nothing happens]fatal: refusing to merge unrelated histories
   

the default pull uses no-rebase.

6.2 Rebasing

rebasing just means we can reorder our commit history

6.3 remove files after adding .gitignore

Scenario:

  • You acidentally pushed .bleh on to github
  • Later you commited to local repo a .gitignore to ignore .bleh then synced it with github repo
  • Problem: .bleh is still in the github repo

Solution:

  • git rm -r --cached .bleh then add,commit and push to github will remove the remote github .bleh file or directory

7 git Vim micro tut for merging

ctrl-w-w to switch screen on 3 way commit

esc-u to undo esc-:diffupdate after undo to rescan difference

esc-:diffget LO to apply Local, left most window esc-:diffget remote to apply Remote, right most window esc-:diffget BA to apply base, top center window

esc-i to insert mode esc-dd to delete line

esc-]-c to move to next conflict esc-[-c to move to prev conflict