Giter Club home page Giter Club logo

learning-git's Introduction

Resources:

1. Happy Git and GitHub for the useR(https://happygitwithr.com/)

2. Learn Git Branching (https://learngitbranching.js.org/)

3. Git - the simple guide (https://rogerdudler.github.io/git-guide/)

4. Oh,shit! Git?! (https://ohshitgit.com/)

Git Locals

  • One of the most common ways I use relative refs is to move branches around. You can directly reassign a branch to a commit with the -f option. So something like:

git branch -f main HEAD~3

moves (by force) the main branch to three parents behind HEAD. im1 im2

  • git rebase main

im3 im4

Now the work from our bugFix branch is right on top of main and we have a nice linear sequence of commits.Note that the commit C3 still exists somewhere. Now to merge the main branch onto bugFix:

im5

  • Relative commits are powerful, but we will introduce two simple ones here:

Moving upwards one commit at a time with ^. Moving upwards a number of times with ~<num>.

  • im6

git branch -f main HEAD~1. force move the main branch 1 level up the current HEAD.

im7

git checkout main

git merge C6. merging main branch to a specific commit.

im8

moving the bugFix branch to specific commit. git branch -f bugFix HEAD~4 image9

  • git reset

image10

  • While resetting works great for local branches on your own machine, its method of "rewriting history" doesn't work for remote branches that others are using. git revert.

image11

  • git cherry-pick

image12

That's it! We wanted commits C2 and C4 and git plopped them down right below us. Simple as that!

image13

  • When the interactive rebase dialog opens, you have the ability to do two things in our educational application:

You can reorder commits simply by changing their order in the UI (via dragging and dropping with the mouse).

You can choose to keep all commits or drop specific ones. When the dialog opens, each commit is set to be included by the pick button next to it being active. To drop a commit, toggle off its pick button.

It is worth mentioning that in the real git interactive rebase you can do many more things like squashing (combining) commits, amending commit messages, and even editing the commits themselves. For our purposes though we will focus on these two operations above.

  • Here's another situation that happens quite commonly. You have some changes (newImage) and another set of changes (caption) that are related, so they are stacked on top of each other in your repository (aka one after another). The tricky thing is that sometimes you need to make a small modification to an earlier commit. In this case, design wants us to change the dimensions of newImage slightly, even though that commit is way back in our history!!

image14

git rebase -i main and change sequence of C2 and C3

image15

image16

git commit --amend

image17

git rebase -i main (to rechange location of C2 and C3)

image18

git checkout master

git merge caption

image19

  • Git tags support this exact use case -- they (somewhat) permanently mark certain commits as "milestones" that you can then reference like a branch. More importantly though, they never move as more commits are created. You can't "check out" a tag and then complete work on that tag -- tags exist as anchors in the commit tree that designate certain spots.

Screenshot from 2021-09-08 14-36-06

  • Because tags serve as such great "anchors" in the codebase, git has a command to describe where you are relative to the closest "anchor" (aka tag). And that command is called git describe!cGit describe takes the form of:

git describe <ref>

Where is anything git can resolve into a commit. If you don't specify a ref, git just uses where you're checked out right now (HEAD).

The output of the command looks like:

<tag>_<numCommits>_g<hash>

Where tag is the closest ancestor tag in history, numCommits is how many commits away that tag is, and is the hash of the commit being described.

Screenshot from 2021-09-08 14-42-35

  • Upper management is making this a bit trickier though -- they want the commits to all be in sequential order. So this means that our final tree should have C7' at the bottom, C6' above that, and so on, all in order.

Screenshot from 2021-09-08 15-23-32

git checkout bugFix git rebase main

Screenshot from 2021-09-08 15-24-43

git checkout C4 git rebase bugFix

Screenshot from 2021-09-08 15-26-37

git checkout C5 git rebase C4' git checkout side git rebase C5' git checkout another git rebase side

Screenshot from 2021-09-08 15-29-41

git checkout main git merge another

Screenshot from 2021-09-08 15-30-38

  • Branch one needs a re-ordering of those commits and an exclusion/drop of C5. Branch two just needs a pure reordering of the commits, and three only needs one commit transferred!

    Screenshot from 2021-09-08 16-03-26 git checkout one git cherry-pick C4 C3 C2

    Screenshot from 2021-09-08 16-04-41

git checkout two . git cherry-pick C5 C4 C3 C2

Screenshot from 2021-09-08 16-06-14

git branch -f three C2 Screenshot from 2021-09-08 16-07-16

Git Remotes

  • Technically, git clone in the real world is the command you'll use to create local copies of remote repositories (from github for example). We use this command a bit differently in Learn Git Branching though -- git clone actually makes a remote repository out of your local one. Sure it's technically the opposite meaning of the real command, but it helps build the connection between cloning and remote repository work.

  • Well, remote branches also have a (required) naming convention -- they are displayed in the format of:

<remote name>/<branch name>

Hence, if you look at a branch named o/main, the branch name is main and the name of the remote is o.

Most developers actually name their main remote origin, not o. This is so common that git actually sets up your remote to be named origin when you git clone a repository.

Screenshot from 2021-09-09 08-17-09

  • In this lesson we will learn how to fetch data from a remote repository -- the command for this is conveniently named git fetch. You'll notice that as we update our representation of the remote repository, our remote branches will update to reflect that new representation. This ties into the previous lesson on remote branches.

Screenshot from 2021-09-09 08-22-02

Screenshot from 2021-09-09 08-22-54

Screenshot from 2021-09-09 08-27-21

Screenshot from 2021-09-09 08-28-37

git fetch downloads commits/data for all the branches.

Screenshot from 2021-09-09 16-38-31 Screenshot from 2021-09-09 16-41-31 Screenshot from 2021-09-09 16-46-44

  • updating remote branches

Screenshot from 2021-09-17 13-58-59

git commit

Screenshot from 2021-09-17 13-59-56

git pull

Screenshot from 2021-09-17 14-00-54

  • git push is responsible for uploading your changes to a specified remote and updating that remote to incorporate your new commits. Once git push completes, all your friends can then download your work from the remote.

Imagine you clone a repository on Monday and start dabbling on a side feature. By Friday you are ready to publish your feature -- but oh no! Your coworkers have written a bunch of code during the week that's made your feature out of date (and obsolete). They've also published these commits to the shared remote repository, so now your work is based on an old version of the project that's no longer relevant.

In this case, the command git push is ambiguous. If you run git push, should git change the remote repository back to what it was on Monday? Should it try to add your code in while not removing the new code? Or should it totally ignore your changes since they are totally out of date?

Screenshot from 2021-09-17 14-13-28

git pull --rebase git push

Screenshot from 2021-09-17 14-18-27

But with git pull then git push

Screenshot from 2021-09-17 14-20-34

  • Next Topic:

    140814368-22a70033-78a2-4349-9bd6-e7e6cd1338a2

Input

7fe192f6-fe2f-4ca0-865e

Output

d80950e7-33ee-43da-a7bd

step 1: git checkout main; git pull --rebase

abbf1438-c346-4151

step 2: git checkout side1; git rebase main

6b28a044-6053

step 3: git checkout main; git rebase side1; git push

89aa44b73-f3ef

step 5: (we can also use cherry-pick) git cherry-pick C3 C4; git push

220a-4db6-8df3-

step 6: git cherry-pick C5 C6 C7; git push

b7cd2455-9dac-44f2

Easier way to complete this:

  • git fetch (fetch C8 from remote and point o/main to C8)
  • git rebase o/main side1 (rebase side1 on top of o/main)
  • git rebase side1 side2
  • git rebase side2 side3
  • git rebase side3 main
  • git push

Rebase vs. Merge

There's a lot of debate about the tradeoffs between merging and rebasing in the development community. Here are the general pros / cons of rebasing:

Pros:

Rebasing makes your commit tree look very clean since everything is in a straight line

Cons:

Rebasing modifies the (apparent) history of the commit tree. For example, commit C1 can be rebased past C3. It then appears that the work for C1' came after C3 when in reality it was completed beforehand.

Some developers love to preserve history and thus prefer merging. Others (like myself) prefer having a clean commit tree and prefer rebasing. It all comes down to preferences.

  • We can do the above mentioned problem but with git merge
$ git fetch
$ git checkout main
$ git pull
$ git merge side1
$ git push
$ git merge side2
$ git push
$ git merge side3
$ git push

54394822-ffae

  • You can make any arbitrary branch track o/main, and if you do so, that branch will have the same implied push destination and merge target as main. This means you can run git push on a branch named totallyNotMain and have your work pushed to the main branch on the remote!

There are two ways to set this property. The first is to checkout a new branch by using a remote branch as the specified ref. Running

git checkout -b totallyNotMain o/main

Creates a new branch named totallyNotMain and sets it to track o/main.

41d8-4fe8-9844

Another way to set remote tracking on a branch is to simply use the git branch -u option. Running

git branch -u o/main foo

will set the foo branch to track o/main. If foo is currently checked out you can even leave it off:

git branch -u o/main

Input

2e5094fb-5d1d

Output

ab65-468c-be69

$ git checkout -b side
$ git commit
$ git branch -u o/main
$ git fetch
$ git rebase o/main
$ git push

Git push

git push can optionally take arguments in the form of:

git push <remote> <place> Ex: git push origin main

translates to this in English:

Go to the branch named "main" in my repository, grab all the commits, and then go to the branch "main" on the remote named "origin". Place whatever commits are missing on that branch and then tell me when you're done.

By specifying main as the "place" argument, we told git where the commits will come from and where the commits will go. It's essentially the "place" or "location" to synchronize between the two repositories. Keep in mind that since we told git everything it needs to know (by specifying both arguments), it totally ignores where we are checked out!

Input

fe25a8e8

Output

59a69ad2

  git push origin main
  git push origin foo

You might then be wondering -- what if we wanted the source and destination to be different? What if you wanted to push commits from the foo branch locally onto the bar branch on remote? n order to specify both the source and the destination of , simply join the two together with a colon:

git push origin <source>:<destination>

This is commonly referred to as a colon refspec. Refspec is just a fancy name for a location that git can figure out (like the branch foo or even just HEAD~1).

b0c18c2c

4ce55968

Input

585e11af

Output

70d18b4d

step 1: git push origin foo:main

bdb5bae0

step 2: git push origin main^:foo

Solved!

Git Fetch

The arguments for git fetch are actually very, very similar to those for git push. It's the same type of concepts but just applied in the opposite direction (since now you are downloading commits rather than uploading).

If you specify a place with git fetch like in the following command:

git fetch origin foo

Git will go to the foo branch on the remote, grab all the commits that aren't present locally, and then plop them down onto the o/foo branch locally.

You might be wondering -- why did git plop those commits onto the o/foo remote branch rather than just plopping them onto my local foo branch? I thought the parameter is a place that exists both locally and on the remote?

Well git makes a special exception in this case because you might have work on the foo branch that you don't want to mess up!! This ties into the earlier lesson on git fetch -- it doesn't update your local non-remote branches, it only downloads the commits (so you can inspect / merge them later).

"Well in that case, what happens if I explicitly define both the source and destination with <source>:<destination>?"

If you feel passionate enough to fetch commits directly onto a local branch, then yes you can specify that with a colon refspec. You can't fetch commits onto a branch that is checked out, but otherwise git will allow this.

Here is the only catch though -- is now a place on the remote and is a local place to put those commits. It's the exact opposite of git push, and that makes sense since we are transferring data in the opposite direction!

That being said, developers rarely do this in practice. I'm introducing it mainly as a way to conceptualize how fetch and push are quite similar, just in opposite directions.

d349e918

6a5f7ca2

Git Alias

Edit ~/.gitconfig to add these useful developer shortcuts.

[core]
	editor = code --wait
[alias]
	co = checkout
	cob = checkout -b
	cp = cherry-pick
	go = clone --recurse-submodules -j8
	kill = branch -D
	last = log -1 HEAD
	lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
	ll = log --pretty=oneline --graph --name-status
	pr = !git fetch --all --prune && git prune && git remote prune origin
	spull = !git fetch --all && git pull --rebase && git submodule sync --recursive && git submodule update --init --recursive
	spush = push --recurse-submodules=on-demand
	sup = submodule update --recursive --init
	sync = !git fetch --all && git pull --rebase origin master
	undo = reset HEAD~1 --mixed
	unstage = reset HEAD --
	wipe = clean -Xfd
	amend = commit --amend
[diff]
	submodule = log
[fetch]
	recurseSubmodules = on-demand
[init]
	templatedir = ~/.git-templates
[pull]
	rebase = true
[status]
	submoduleSummary = true
[rerere]
	enabled = true

Want to squash commits before merge request

This will create a interactive rebase against master branch.

naz@pop-os ~/git-cloned-repos/psf-vsn/sources/row-guidance (naz/save-data-csv)

$ git rebase -i origin/master

Or you can do interactive rebase against a specific commit:

$ git rebase -i commit-hash

Then you pick all the commits that you want to squash as s and leave the commit as pick where you want to squash all the commits.

Manually do accept incoming change or accept current change if git can't figure out which one to accept. Then:

git rebase --continue

Then you have to force push the new commits on your branch at origin

$ git push -f origin naz/save-data-csv

Messed Something Up!

git reflog

This will show all the changes made with commit. Find the commit hash where you were correct. Then

$ git checkout commit-hash -b backup/dar/naz

This will create a new branch named backup/dar/naz with the commit where you were correct. Delete the branch where you messed up.

git kill messed-up-branch

Now, you want to change the name of the current branch (backup/dar/naz) into old brach(messed-up-branch), because old branch is probably pushed to origin and you want to follow that that or keep comments of merge request.

Be on your current branch and rename it to old branch:

git branch -m messed-up-branch

Then force push this branch to origin where the original branch was:

git push -f origin messed-up-branch

learning-git's People

Contributors

sudokhan112 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.