Git Crash Course - Full Tutorial For Beginners
Skills:
ML Pipelines60%
Key Takeaways
Using Git for version control with GitHub repositories and basic commands
Full Transcript
Today we're going to do a crash course on Git, the most important tool for every programmer, not GitHub, not GitLab, but Git, the core version control system. This is a must-have skill for anyone who has anything to do with coding. If you are a developer, if you want to be a developer at a company, if you want to be a freelancer, if you want to be self-employed, if you want to have a startup, and you want to be the tech guy, the moment you do anything with code, you need to be fluent at Git. You should be fluent at Git. And this video today is going to be a complete crash course, which means we're going to start from the very basics and fundamentals, like what is Git, what is a version control system, why do I need it, and we're going to cover the most important features, like committing, merging, rebasing, pushing, pulling, and stuff like this, just the basics of Git. We're going to learn everything that you need to know, and we're going to do it quickly and efficiently. So, if you like this video, let me know by hitting the like button and subscribing, and now let us get right into it. >> [music] >> All right, so we're going to do a crash course on Git today, the most important developer tool, in my opinion. Anyone who touches code should know how to use Git and should be using Git, whether you work alone on projects or whether you collaborate with other people, then definitely you should be using Git. And this is what we're going to learn about today, the version control system Git, which is basically a command line tool. We're going to cover the functionality here in a second, but don't confuse Git with GitHub, with GitLab, with Bitbucket, which is also a Git solution, with Gitea, which is self-hosted. These are all platform solutions, applications building on top of Git, building around Git, so to say. Git itself is what drives the functionality, and these are just platforms where you can host repositories, you can do things like pull requests, but the core functionality that we're going to cover in this video today is going to be Git. However, when you're using Git, usually you're using some repository that you're hosting on GitHub or on GitLab. So, of course, we are going to use these platforms. In my case here, for the sake of simplicity, because it's the most common one, we are going to use GitHub, but this is not a GitHub specific tutorial today. This is a Git crash course, but we're going to use GitHub as the platform where we're going to work with the repositories. But before we do any of that, let me briefly show you or explain to you just in theory what Git is actually for. And for this I'm going to go into any directory, in my case the tutorial directory here, and let's say I start a new project now. I want to work on a project locally on my system here. Let's call this my project and I start this project. Maybe I create a main.py file and maybe or actually before I do that, maybe I do UV init or something, UV at flask and then I go into main.py and I start typing some code like from flask import flask, whatever. I do my thing and at some point I have an MVP version, something works and I'm happy with it. So this application works. Now what could I do to make sure that I keep this version of the project somewhere and if I mess it up, I can go back to it. Of course I can just go out of this uh folder, I can go back in the in the file directory so to say and I can just make a copy of it. And of course for those of you who don't like the terminal, you can do all of this in the graphical user interface. I can just copy, paste and rename this to be my project backup. And every time I make a major change, I can just make another one of these directories. My project one, my project two, my project backup 2026, uh 23rd of January or or actually of June in this case. Um and basically I can I can manage my backups like this. And what do we do if we want to collaborate with someone? I can get a USB drive, I can upload it to Google Drive, I can send it via email and they can then work on it and send me back the project. Of course this is not how developers work. We have something called a version control system. This is exactly what Git is for. The idea is that every time I do something, every time I make progress with my project, I can basically go ahead and commit the current state. I can upload the current state somewhere into a repository and then I can keep working on it, upload the next version and it's all incremental changes that that are happening to this project. And when someone else works on that, they can also do the same thing. And the moment we want to join everything, we can have some conflicts, and they need to be resolved, but that is the idea. And instead of just showing this in the command line, let me also visually show you what the idea of Git is in terms of um how it's visualized. Let's say that I start my project at a certain state. So, here maybe I have a main.py script. This is my initial state, we could say. And what I want to do from there is I want to publish a change, and then I have this next state. Now, I have maybe a nice login screen. And then maybe I want to add a sign-up as well. So, then I have a next version and a next version. And we could say that this whole thing, this whole history of developments here is called a branch. And I can say this is the main branch, this is the most important branch. This is where all of my work happens in a graph. We're going linearly from start to end. Now, let's say we have multiple people working on the same project, and I'm saying now that I want to start working on a feature that is UX-based, and someone else wants to start on a database feature. We're working on the same codebase. So, instead of just both continuing into different branches and then shooting off into into different projects, we can basically do that, yes, but we can also then merge it back. And this is something we're going to cover later, but the idea is maybe now I develop a one, start a UX branch here, and I continue to push updates here. I have my own states. I continue maybe adding a button, maybe changing the style, maybe adding uh new UI elements, and someone else meanwhile starts another branch here called DB or something. And they're also working on their features. And maybe someone just continues to work on main as well, because maybe they didn't listen to the conventions, or maybe there's a reason for them doing that. At some point, I'm done with my changes. And what do I want to do then? I want to merge back into main. So at this point I'm saying, "Okay, I'm done with my UX changes. Let me go back." Someone else does the same thing. Let me go back and then we work on the same code base. And the moment something breaks, the moment I have a problem, I can just go back in history, see what happened, I can roll back, I can do a bunch of different things. And before this gets too confusing, this is the idea of a version control system. I don't just work with copies of directories. I have diffs, so I can always see what is the difference between this state and the state 2 days ago. I can go commit by commit, see who did what, and then we just have a nice history. We can see who did what and why something happened, and we can always go back, we can always change stuff, we can always resolve conflicts. If in the merging process something goes wrong, we can always resolve that. And this is what we're going to learn how to do in the command line in this video today. So that's going to be very educational for those of you who don't know about Git, and also for those of you who know a little bit about Git. Maybe you know how to add and push and pull, but that's it. We're going to cover also more advanced functionality today. Now, if you know what you're doing to some degree, you can use whatever platform you like. Feel free to use GitLab, feel free to use Bitbucket or Gitea if you want to self-host it. I'm going to go with GitHub for this tutorial today, and the next thing that we want to do is we want to create a repository online that we can then clone onto our system. This is going to be the first command that we look at, Git clone, cloning the repository from remote to our system, having a copy of it, making some edits, pushing the updates, so we can then update the online state as well. And then we're going to see how to take it from there and continue with additional commands. But what you need to do is you need to create a GitHub account if you want to follow along with this tutorial. You want to go to your repositories tab, and there you want to create a new repository. So just new, and then you can give it a name. Let's go in my case here with Git crash course. And we can provide a description if we want to. The only thing I'm going to change here is the visibility. I want to set it to private just to show you that we need the authentication. If you set a repository to private, you cannot just clone it without authentication, without credentials. So, we're going to have to set this up. Of course, this is going to be GitHub specific, but we need to do it somewhere. So, that's what we're going to do here. We're not going to add any default file. So, we're going to start with a blank repository. So, I'm just going to click here, create repository. And then what we need is we need to somehow get this onto our system. We can do that with Git Clone, but for this we need credentials. So, what we're going to do here is we're going to go to our to this profile button here and we're going to go to settings. I'm going to open this in a new tab. Now, if you want to do it the easy and professional way, I would recommend sending up setting up SSH keys. I do have a video on this channel where I show you how to do that. But since this is not a GitHub tutorial, but a Git tutorial, I want to keep it simple here and just go with the access tokens, just to keep it simple. So, we're going to go here to developer settings. I'm going to go to personal access tokens. I'm going to say tokens classic. I'm going to say generate new token. I'm going to go with classic, just so we have a general use token, super simple. And I'm going to call this crash course token. Like this. I'm going to say it expires in 7 days, actually. I'm going to check all these boxes, not because we need them, just because I don't want to make a tutorial here about the GitHub specific aspect. So, we're just going to go with a fully permissive Of course, this is risky if you don't know what you're doing. If you have a proper GitHub account, probably you're not watching this video then. But if you don't know what you're doing and you have an important code, don't do that and be careful with that key. But for the sake of simplicity, I'm just going to give all the access here, just so we have a very powerful token. I'm going to generate it. And then I'm going to copy it. So, this token is important. Copy it, put it somewhere where you can use it, where you can later use it in the connection. And now essentially what we need to do is we need to just use HTTPS here. So, click here in the quick setup to HTTPS. This is the URL of the GitHub repository. So, we can copy this, open a terminal, navigate to any directory that you want to be working in. In my case, that's going to be my tutorial directory. And here now we're going to run the first Git command. Now, of course, if you don't have Git on your system, you need to have Git on your system. Most systems have Git. If you're on Windows, you have to install it manually. Just go to the website or use your package manager, install Git. On basically all Linux systems, it should be installed out of the box. On Mac, I think too. I'm not sure about this. But, the installation is super simple. Just go to Git and or just type Git download in Google. You will find the link immediately. And once you have Git installed, you just say Git clone and this URL. Now, the problem is this won't work without the token. So, if I just run this now, this will say username password and it will tell me that this is no longer supported. So, what you actually need to do here, you need to say Git clone again. But, we need to also provide our token now. So, I'm going to go back here to the token which generated. I'm going to copy it and I'm going to say now token @github.com and then the same thing. So, it's going to be https uh colon slash slash this token @github.com. And now we're cloning the empty repository, but it worked. We now have, if I go to uh Git crash course, I have here my Git repository. So, this worked. Now, if you at some point want to invalidate this token, you can do that. You can just go to your personal access tokens. You can just click on tokens here and this will show you the token. You can just delete it and then it can no longer be used if anyone saw your token, which is of course what I'm going to do here after this video. So, you cannot use this token using my GitHub. But, uh that is the basic idea. So, now we can close this. We now have this cloned. And cloning means we have the same version locally as online. So, this here, the Git crash course repository uh contains nothing and our local version here also contains nothing. So, what we can do is we can start by creating something. Now, for this you can use your GUI, you can use an editor. I'm going to use a terminal editor. Don't be confused by that. I'm using Neovim. So, whenever I say NV and some file, this is not a Git command. This is just me opening a file in my editor here. So, you can do the same thing in VS Code. You can also use your basic your basic file manager to create files here if you want to. I'm just going to do it in the command line. Don't be confused by that. This is not a Git related command. So, let's go and say we're going to to create a readme.md, a readme markdown file. And here I'm just going to say hello. That is going to be my first file. How do I now take this file and push it onto the repository? How can I actually put this online so I can also see it here and I can clone it from somewhere else? I can do that by first adding the file. Now, if I don't know what file I can add, I can also run the first command, which is Git status. Git status tells me what's going on right now. It tells me I'm currently on a branch called main. This is, remember, in the my pain visualization, this is the white line that we saw, the main branch that basically contains nothing as of right now. No commits yet, and it also says untracked files. I can add these files using Git add. So, basically, if I say now Git add readme.md, this now added the file to be committed. So, if I say Git status again, you can see changes to be committed, new file readme.md. Now, you might be wondering why do I have stuff like this here. This is just my terminal setup. This is not a Git feature. If you don't have this, don't don't be confused. This is not default Git up This is not the default Git installation. This is just my Z shell format, so this is just a nice UI thing. And, as you can see now, this file is staged to be committed. What can I do now? I can actually commit the file. I can say Git commit. And if I say Git commit, this basically means now we're done. This file is actually committed to the Git repository, but I can also provide a message. So, I can say {dash} m and then I can say this is my first commit, if you want to do it like this, or if you want to be more professional, you would do something like initial commit is what you usually call this one. So, get commit and then initial commit. Now, this is committed. We have everything uh up to date. So, get status. So, you can see it tells us here nothing to commit. And if I want to now sync what I have locally with what is happening online, remotely, I just have to push the changes. So, this basically means I'm taking my current local state and I'm pushing it onto the online state. So, I can say get push and then you can see that this actually pushed the main branch onto the main branch. Now, since this was a new empty repository, the main branch didn't even exist. It only existed uh locally. Now, we created it globally or uh remotely, we should say. And if I reload this page now, you can see that I no longer get the instructions. I get a repository with content. I have a readme.md file, which in the case of GitHub is also rendered directly, but that is now my repository. I have a branch. This branch is called main and on this main I have one commit. On this main branch, I have one commit and this one commit here was creating What was the message? Initial commit and was creating a um hello.md file. Now, maybe you encountered a problem with a name and email setup because by default Git doesn't know your configuration. With Git, you can provide a name, you can provide an email, and you can see what currently is your name and email by saying Git config and then user.name. In my case, that's NeuralNine. If I say user.email, that's social@neuralnine.com. If you don't have this in the beginning, you have to set it. So, you basically have to run the same command Git config username followed by something. This would change now the username the next time I run this, my name is something. And if I change email, this will also work. In my case, of course, I want to keep my current setup, so I'm not going to call this uh something. I'm going to go and say that I want to be named NeuralNine again. That is exactly what you will also see here in the commit. Uh the problem is that this can also be done you can also commit stuff not as NeuralNine and social NeuralNine even though that's my GitHub uh credentials. I can also do that as Claude and then it shows up that Claude Code committed to this um repository. So, that's also something that can happen. So, make sure you set this up properly. This is what you can do with Git config. But yeah, now you know how it basically works and we can do more things. So, I'm working on the same repository. I have the readme.md file. First of all, I can create an additional file. Let's call this one test file. And here I'm going to also say hello world. We're going to keep it very very stupid and simple. We're not going to concern ourselves with any code here just to show you how this works. And also what I want to do is I want to make some changes. Maybe I want to uh just add a new line or new line of content and also blank line. So, again what can I do? Git status. Now you can see I modified readme.md and I also created test file. And now also to show you something else, let's create another file and just uh say test in here like this. What I can also do and it also tells me that is first of all, I can add it and once I add it, I can also un-add it. I can also remove it. So, if I say Git add and I can add the test file and I can add another file. Now if I say Git status, you can see that these new files are committed but readme is uh but but readme here is still not committed uh or still not uh added to be committed, still not staged basically. And what I can do here is I can also restore. I I say Git restore {dash} {dash} staged and another file. And in this case, that means that this is again not added to be committed, but this remains added to be committed. Now, keep in mind that this restore and this restore with staged are different. If I say git restore staged test file, this just means I'm not adding it for this commit. If I say git restore the unadded, the not yet added readme.md, it means it reverses my changes. So, if I say git restore uh just readme.md like this, then the content of readme.md is back to the beginning. I don't have my new line anymore. So, I have to add it manually back. And now if I say git status again, we have it here. I can also say git at readme.md. And now if I say git status again, you can see it's now modified and added. I can also now do git restore staged. This would just remove the status that it's uh added for commitment. But, that is the basic idea. Now, I can do again git commit. I can also not provide a message. I can just say git commit. This will open an editor where I can provide a commit message. So, I can say my message here, for example. Then I just have to close and save. This is now um committed with my message here. And again, to actually make this happen, I need to say git push. And this is pushed online. If I reload now here, you can see I have a second commit, my message here. I can go into uh the code, and you can see I have every new stuff here. If I go to the commits themselves, I can also see a diff view, so I can see my message here uh what changed. I added two new lines here, and I added a new file here. So, these are the exact diffs. This is what we call a diff. This is the change, the difference between what was there before and what is there now. And we can also do that in the command line. So, for example, if I just create or if I go to readme.md and add yet another line, I can also run the git diff command and uh see what's happening. So, I can just go readme.md and you can see here yet another line is the diff. I can also say git diff in general and then I can see what the difference is between my current state and the state on the remote repository. Now, let's talk about another aspect of Git, which is pulling stuff from a repository. Up until this point, we've only been pushing stuff to the repository. Yes, we cloned it in the beginning, but it was basically empty. What do I do if someone else changes something on this repository? So, for this, I'm going to open up a second terminal, go to the same tutorial directory, and I'm going to create another directory here called other person, imagining this is on a different system, and I'm going to do the same thing. I'm going to clone this repository. Now, of course, I don't have my token anymore, so how can I do that? I can still go to this repository that I have here, and this is going to show us now a different command, which is git remote. Git remote is obviously for working with stuff that is remote, and here I can just say get URL origin. Origin is basically the URL of my GitHub repository here, and this, as you can see, also contains my token. So, I can actually use this and do the git clone command using that exact URL. So, git clone like this, and I have the same repository cloned in this directory. Now, of course, not empty, but with the current state of things. So, readme.md and test file. So, if I change something here, let's say this other person now creates some change in readme.md, uh I make a change here, whatever. Then, of course, what I can do is I can get at the change that I made with readme.md, and now I need to move this, so I'm not blocking this with my camera. I now add readme.md, I commit what I did, so I'll just get commit -m uh update. Of course, use commit messages that are not that stupid. You should always explain what you're doing. But, git push to put it online, and now, of course, this directory is behind. I don't want to clone this every time from scratch. I don't want to go and always say get clone to to get the current state. I just want to pull the difference. So, what changed and just apply these changes locally. So, I could just say get pull and get pull will basically do exactly that. It will download the changes and it will tell me it and it will apply them to my local repository. However, you can see and this is actually good that it's happening now even though I didn't plan for this um that by pulling I'm overriding my local changes. So, if I go and look at readme.md I have stuff here that I didn't commit yet. And also if I go to get status, you will see that I modified my readme.md but now I'm trying to pull the new version of a readme.md so there's a conflict there. Um the easiest way to resolve it is to just ignore my my stuff. To just say get restore readme.md and to say I'm losing my changes. So, yet another line is gone now. And of course let me move this here so you can actually see this. So, I just did uh get restore readme.md and then I can do get pull and now it works. And you can see two lines were inserted into readme.md. So, now if I take a look at readme.md I have the current state. Now, sometimes I might not want to do this. I might just want to know if something is happening. So, again let's say I go once again into readme.md another change here. I add and commit. So, I'm going to do it again get add readme.md get commit more changes. Get push. Now it's online. And I want to know if something changed but I don't want to actually pull it. I don't want to actually apply this. What I can do then is I can say get fetch. Get fetch basically tells me is there something new? So, if I do get status, you will see your branch is behind origin/main by one commit and I would have to do get pulled to actually update my local branch, but I didn't do it yet. I just got the information because if I don't do get fetch, I don't even know that I'm not up to date. So, I actually need to load the current state online, the current state on the remote branch to know if everything is up to date locally. So, in this case, not a problem. I can just say get pulled and I get everything onto my system again. If I want to see by the way the history of what I'm doing here all the time, I can just say get log and it will show me everything. I will see all the commits and I will also see what I'm currently looking at. The head is pointing to uh the main branch, the current commit here, more changes, the one that we pushed here, is now also the current state here. Now, we're always talking about branches here and we were also talking about branches individualization. Remember, we can just start we can branch off the main branch and do our own thing independent from all the other developers and at some point go back and merge into main. Let's take a look at how this works. How do we create a new branch using Git? We do that by just saying Git switch or if you just want to create it, there's also a Git branch command. We're going to talk about this in a second as well. But, Git switch creates a branch and also switches to the branch right away. So, if I do Git switch and then {dash} {dash} create, let's call this now feature one. This would create a branch called feature one and it would also switch to that branch. So, by doing that, you can see switched to a new branch feature one. And here now, I can do a bunch of stuff. I can for example commit now my another file that I created. Remember, I created another file, never added it, never committed it. So, I can do that. Add another file. I can also create one more and put hello in there. And I can also say get add one more. Uh and now I can just go ahead, commit this. Let's call this feature one work or something like this. And I can push. Now, when I push, you will see that there is a problem that we didn't set an upstream branch. What this essentially means is that locally now on my local installation here or on my local in my local repository, I created a branch called feature one. Online, remotely on GitHub, there is no branch called feature one. So, what am I trying to do now is I'm trying to push, but it doesn't know where I want to push to. And if I want to push to an online branch remote branch called feature one, I need to create it, but I can also push to a branch called feature 22, even though locally it's called feature one. I just need to specify this. So, what I need to do actually is I need to say get push {dash} {dash} set upstream and then origin feature one. This creates a branch on the origin remote uh called feature one. And now I'm pushing to it. So, how does this look in GitHub? It looks like this. I go to my code, and you won't find the new push. You won't find any new file here, but you can see here feature one have recent pushes. Here I can switch to another branch. I can go to feature one, and there I have a different state of the code base. So, to visualize this maybe here again because I think visual explanations are always easier to understand, we have a main branch. I started here. I started here. And multiple people are working on this. So, let's say we have a person here um and then let let's say we have a turquoise person down here. These are the two directories, and let's say the first commits were all from him, but then this guy committed something and then committed something else and then this guy commit something else again. Whatever, that's all linear. All of this is happening on a branch called main. So, that's super simplistic. What we did now is one of those, let's say the yellow one, branched off and did something else. Created a branch called feature um feature one. So, everything this person does now can happen on this branch separately. The main branch remains the same. The current state of the main branch is this here. And whatever I do on feature one doesn't concern me. By the way, the other person can do the same thing. They can branch off on their own, call this feature two. I'm just going to write F2 now, and do the same thing. If I want at some point, I can merge back into main. So, I can basically say create a commit at this point where this merges back into main. I can do that. I don't have to do that, but right now they're working on separate branches. So, if we want to do this, this one is working on feature one. This guy here can just do get switch. Let me move this again here. Uh like this, the tiling window manager is getting a little bit messy. So, let's clear the command line here. What I can do is I can create a branch. Now, let's not do this with get switch. Let's do this with get branch. So, first of all, with get branch {dash} {dash} all, I can see what branches I currently have. You can see that this person, the second one basically, doesn't see any feature one branch because they didn't fetch and they didn't pull. So, if I say get fetch, new stuff is happening. Get branch all, now you can see there's an origin branch feature one. I don't have it locally. So, let's go and say get pull. And if I go and say get branch all, you can see now that currently I'm operating on feature one. Now, I don't want to be in feature one. I want to do my own thing. I want to create a branch which is called feature two. So, get branch feature two is basically the same as get switch create just that we don't switch. So, we created the branch, but we're not on this branch. So, now I can say get switch feature two, but I don't need to create flag anymore. So, now I'm on feature two. I can do my own stuff here. And actually, I made a mistake because what did I do now? I created the branch feature two branching off from feature one. This is not what we showed here in my paint. I actually wanted to create it from uh this state here, not from this state. So, what can I do to reverse this? I can just say I first have to to switch to a different branch. Let's switch to main. Then I can say get branch {dash} D feature two to delete the branch. And here it asks me if I really want to do this. I have to do uppercase D. That's fine. So now I deleted the branch. And I can say again get branch feature two going from main. And I can say get switch feature two. So now you can see that this takes the state of main, not the state of feature one. Now I can say here F2 file or something. Hello world. Not very creative here today. Clear. And then I can say get add F2 file, get commit. And then I can say F2 work or something. Get push. Same thing as before, set up stream has to be origin feature two. Or it doesn't have to be, but makes sense to put it like this. And now again on GitHub, I can reload. We can see on feature one I have one state of things. On feature two, I have another state of things. And on main, I still have the very basic initial state that feature one and feature two branched off from. Now this might be a bit difficult to just understand in your head. So let's use a visual Git client. You can install this if you want to, or you can just watch it here for the tutorial. We can use Git G. This is a Linux visual Git tool. I can just go here and go into my thing. Let's switch to main. And I can say Git G. So this opens it in this directory. And you can see it visualizes here my main branch. I have my feature branch, my feature two branch. And I can also go to all commits. And all commits shows you what this looks like. We have initial commit, all on main, all happening on main here, and then we have the more changes, the last commit on main, and then we can see from main we're branching off into feature one and into feature two. Now, let's see what happens if we continue to commit stuff here. So, I'm going to say here again uh get switch feature one. And it doesn't matter which directory does it now. We're We're just going to keep working on a single one uh to keep it simple. So, now we're the other guy and we're going to say more stuff. And we're actually let's let's stop creating new files. Let's just go and edit readme.md and I'm going to add lines here and maybe I'm going to even remove something. I don't know. Uh let's go and remove the new line here. Get add readme.md, get commit uh stuff. Again, use expressive commits uh commit messages, but I'm going to keep it simple here for the sake of speed. Get push. So, we're constantly doing stuff. We're constantly adding stuff. And in the commit graph, it looks like this now. We have main, we have uh feature one, feature one, feature two. We can also commit directly onto main. So, I can go get switch main and I can continue doing stuff here. So, maybe I want to go into test file and say I made some changes here. This is happening on main now. So, if I say get add test file, get commit stuff on main. Get push. Then, if I run get G, we can see what this looks like. We have main. At some point uh at some point feature one and feature two branched off of main, but then main also continued on its own. So, the question is at some point do we want to go back to main with these new branches? So, let's close this and let's do just one more commit on feature two, so we have a nice graph. Git switch feature two. Clear, and here I'm just going to go into F2 file, add something, write git at F2 file, git commit F2 change, and then git push. And now again, let's take a look at the visualization, and you can see how this looks like or what this looks like now. So again, we have our common state. Here we branch off into three different branches. Main continues on its own. We have Or actually, main is main is here, so right now we're focusing on feature two. So this is what actually looks like. Main is on its own. Feature two is going on its own. Feature one is going on its own. But we can now go back into main. So I can take this feature one, and I can merge it now into main. So I can say here, go back into main. How do I do that? I just have to go uh to the main branch, and I can say git merge feature one. So it says uh merge branch feature one. I can just write this message. It worked. No conflict. And then I can say git push to actually push this online. So first of all now, on GitHub, everything from feature one is now also in main. We combined the two branches. And if you want to see this visually, this is what it looks like. If I go to all commits, you can see at some point here we left the main branch for feature one. I did some work on feature one, and then now all of it runs back into main. And I can do the same thing now with feature two. So I can actually go git uh merge feature two. This means we're taking the stuff from feature two, merging it into uh main. And then I can say git push. I can say git g again. And now everything is back in one branch. Everything is continuing on main. We're all on on same branch. Now, this was very abstract, so let us try something more concrete. Let's actually go into code now. Let's say I have a Python file and I want to say for I in range 0 to 100, I want to print I. That is the code that I want to put on main, so get at main.py, get commit main file, whatever. Get push. Now, this is on main. Now, let's say I branch off from main now. Let's say get switch new feature or let's let's call this feature three to keep the convention. And of course, I need to say dash dash create. And there I make some changes. So, there I go and say main.py first of all has to change from 0 to 200 and then I also want to say to the power of two and um that's basically it. This is the change I want to do here. Get at main.py, get commit um and then I can say changed loop or something. Get push. And now, of course, don't forget to do set upstream origin feature three. Now, this is happening on feature three. The loop is a different one. If I now go back to main and there I want to change something else, so maybe I go and say um we still have the loop from 0 to 100, but I also want to add a print statement later on, so I'm going to say here hello and I also want to send I want to add a message here. I want to say uh output and then the number. So, I'm making other changes. I'm not doing the same changes. I'm making other changes here. And of course, I want to add this. I want to say get commit with a message changed main file. Get push. Now, if I merge, I have a conflict. Both branches did changes on the same file that are conflicting. They're not the same changes. They are uh Yeah, they're conflicting basically. So, if I now say get merge feature three, there's going to be a merge conflict. Conflict merge conflict in main.py. So, what I have to do now in this case is I have to go into main.py and resolve the changes. This is what it looks like. My main.py file is now messed up with Git stuff. We have here what's on head and we have what's on feature three. Here we have the loop for I in range 0 to 100 print output I print hello. Here I have for I in range 0 to 200 print I squared. So, I as a human now have to intervene and say okay, actually I want to keep the loop from 0 to 200. So, I can just manually change that. I want to keep the print hello. I also want to keep the to the power of two. So, I can remove this. I can remove this and I can remove this. So, I manually had to to go with my hands in and change the code. To say this is how I want the code to be when it's merged. So, I can just say that and then I can just say get status. What is the status? I can see that both are modified. So, get add main.py to add this file for commitment or for commit and then I can say get merge dash dash continue. And then I can just write and accept everything. Get pushed to actually put this online and now the merge conflict was resolved. Feature three is merged into main and we can see that our resulting file is what we manually did. And again, if you want to see the visualization of that, this is Now, I actually triggered a sec fault, but this is what it looks like. We have the features from before and now here we branched into feature three. We merged back and this is how we resolved the conflict. If I may for a second, I would like to plug myself in as the sponsor of my own video. If you go to my website neuralnine.com, you will find a tab services and a tab tutoring. Here you can hire me for all sorts of stuff like data science, machine learning, web development. If you need help with something in a project, here you can book me for one-on-one tutoring. If you want me to teach you personally something that you don't understand, if you like my teaching style. On both pages at the bottom, you can contact me via mail and also via LinkedIn. Just wanted to let you know about this. Now, let me move this out of the way now. Maybe we're going to get it back later. But, I want to show you something else, some smaller stuff. How do I deal with wrong commits? Let's say I did something like I created a file here or I edited readme.md, made some change, some change. And then what I do is I add readme.md and I say git commit and then I have some typo in here like this. But, I sent this off already. How do I now reverse my commit? How do I uh change the commit message? I can do that by saying git commit {dash} {dash} amend. Git commit {dash} {dash} amend and here I can edit now my commit message to a proper proper commit message like this. That would be to edit the message. Now I can say git push and the commit message is the correct one if I look into the log, git log, you can see proper commit message was chosen here. But, what do we do if we also want to unstage a file or want to reverse a commit, so the content of a commit? Let's say for example, I made a change in test file, maybe I delete this line and then maybe I also go into readme and delete a line there like this. And now I say git add. By the way, if I want to add multiple files, I can also just add everything with an asterisk or everything in this directory. So, we can use regex and stuff like this. But, um git add would just say now git add asterisk would now just say that I'm adding everything that was changed, so both of these files. Let's say get commit um deletions or something. And now we actually want to reverse this. How do I actually go about this? If I look into get lock, you will see that this is now the current commit that we're pointing the head onto. So, we're actually looking at this. This is our current commit. What I have to do is I have to reset. So, I say get reset {dash} {dash} {dash} {dash} soft, which basically means we're removing the head without unstaging the files. So, get reset soft and then I say head and then basically this wave here this wave here allows me to say go back one one commit. So, I can say reset soft to head one. And now basically the commit never happened. I can still say get restore. I can add more stuff. I can do a commit again basically. If I do get lock, you can see now that we have our head pointing again to proper commit message not to deletion. So, again get commits deletions. What happens if I don't do soft? If I just say get reset, that would now unstage everything. So, if I say get status, it's not even staged. I have to do get add again. Now, let us move on to something very interesting in get or about get called rebasing. And this might be a little bit confusing, so it's okay if you don't understand it the first time. Um but the idea of rebasing is very it's actually not really similar to merging, but the result is the same. So, again, let's say we have a main branch. We have a bunch of these nodes commits here on the main branch, and then we branch off to a a feature branch. So, here I say feature one. I do my stuff. And at some point I would like to to get up to date. So, let's say after I did this branching, something else happened on this branch on the main branch. So, it continued to live independently of my feature branch. Now, merging as we discussed is going back and merging into that branch. Rebasing is rewriting the history of what happened. You can think about this, there's a guy here pulling this like a rope, so taking the main branch and pulling it like a rope back. Um in order to pretend that this branching never happened at this point. To say that this thing here never happened, what actually happened is we waited for all of this to happen here, to to get to the present state, and only then did we branch off into uh this. So, the result is the same, um but not entirely. So, the result in terms of the code is the same because we still have to resolve the conflict, we still have to merge the code, but we're doing it in a different way. So, our branch does not look uh like this. We're not branching off and branching back, we're faking the history so that we're basically, as I said, we're pulling this back to pretend that this is what actually happened, so we end up actually with only the main branch like this. So, it's one straight line. We have a linear history, we never branched off. Of course, we did, but on the graph it doesn't look like that. This has different pros and cons. We can talk about this for a very long time. Basically, if you want a history to accurately uh reflect what's happening, you want to do merging. If you want to work or if you're working with other people, merging is oftentimes the better choice. But rebasing can be just more convenient if you're working alone on a branch and you just want to keep it clean and linear. So, again, rebasing means we're we're applying all the changes. So, how do we do this? Pulling This is maybe the important part here. How do we accomplish this move here from from pretending this? We basically apply these commits here. We apply these main commits onto our current branch. So, we apply the commits that came after our branching to our branching to pretend that we only ever branched off from this point here. So, yeah, a bit abstract. The result is the same. If you just want to know what happens to the code, it's the same. You're merging the same sort of code, but the history will look differently. And actually, I can show you that because we're going to just do the exact same thing that we did before uh with a merge with a main.py file, but we're going to do it with a rebasing. So, I'm going to create here a file main2.py. Uh we're actually going to What was the code exactly? It was for I in range and then it was uh 0 100, we printed I. That was the initial code on main. That's also going to be the initial code now. Git add main2. Git commit some message. Some message like this. Git push. Now, this is on main. Now, we're branching off. Git switch create uh feature4, we're going to call this. And now, I'm going to make a change. So, main2.py, what did we do here? 200. And then, I squared. I think that was the change that we did on the branch. Then, I will say Git add main2.py. Git commit uh some changes. Git push. So, that's now on the feature branch. Again, don't forget the set upstream origin feature4. There you go. And now, we go back to main. Git switch main. And here now, I'm going to go to main2 and do something else. What did we say here? I'm going to say output is I. And I'm I don't really remember what we printed here. Let's just go with hello. That always works. Uh the specifics are not that important, but now I'm going to say main2. Commit again. Commit message. Always doing some random stuff here. Git push. And now, instead of merging, we're going to rebase. So, the idea is I want to say, let's look at the graph. I don't want to do what we did here. I don't want to branch off and merge back. I want to pretend that we never branched off. That this branching actually only happens here. That it happened afterwards. We want to rewrite, want to fake the history. That is the idea of a rebase. And for this, we don't go on a main branch to rebase, we actually go to the feature branch. So, feature four is um course switch get switch feature four. And here we're rebasing main. So, get rebase main. And actually, it doesn't allow me to do that because we do have Okay, let's let's just do get restore uh everything because these are irrelevant. Get rebase main. And you can see here, same as with the merging, we have conflicts. So, now I have to go into main.py and resolve the conflicts the same way we did it before. Nothing changes. The merging merging process is the same. So, I have to say um get 200. Actually, this uh it is different because we're applying the commits one by one. So, we actually have to do this uh step by step, whereas with a merging, we're just merging the two codebases. So, that's the difference, but you're also going to see the difference in in the graph. Um so, let me just do this like that. Let's say one and a half output. So, we still need to manually resolve everything. That stays the same. But now, when I'm done with that, I can say get at main.py and I can say get rebase continue. And now I have to just write get push. And of course, in this case, it doesn't work because what we actually need to do with rebases we need to say force with lease. And that now is feature four. And if we go into git g, if we look at the branch, it looks like we just branched off of main. So, we still have feature four, but it looks like we only started feature four after this commit. So, now the history is entirely different because we apply step-by-step all the commits of the main branch onto feature four to pretend that it never happened uh that the the branching never happened before these changes. All right, and then I only want to show you two more things. One is a very simple thing that we can do called stashing. So, what is the use case for a stash? Let's go get switch uh main branch. Here I'm on the main branch now. Let's say I do some changes. Let's say, for example, I'm working on uh the readme file and I'm making some changes here and uh maybe I add some very complex stuff here. And I'm not done yet. I still want to keep working on this, but all of a sudden someone tells me, uh it's very important that lines is written with a capital L. Please push this ASAP onto the main branch. So, fix this mistake. Of course, in reality, that would be a coding uh mistake. You're working on multiple files. You just want to don't want to lose your changes because you did so many other things, but you're not ready to commit everything yet. Uh but you want to hotfix this line. So, how would I do that? If I look at Git status, I have changes. If I look at Git diff, I have changes. But this needs to change right now. I don't want to commit all the other changes. What can I do? I can say Git stash. Git stash basically now removed all my changes. If I look at Git status, I have nothing here. I have no changes. If I go into readme, everything looks like on the remote branch. And what I can do is I can do the hotfix. So, I can change add lines, fix this, Git status, Git diff. You can see this is the only change I have here. Git add readme.md, Git commit, and let's call this hotfix. And I can say Git push, and everything is fixed. But where are all my changes from before now? Basically, I just have to say get stash pop and I will get them back. So, if I now go into readme, you can see of course now this is in conflict with the upstream. So, we need to make sure that this is resolved because of course we intend to delete this and we intend to keep only that. So, we need to resolve this like a merge conflict, but in general, whatever I do, even if I work on different files, I can always just do stuff like work on test file for example, make some changes here. There you go. Get status and I can always say get stash and everything is gone. Well, in this case, first of all, I need to resolve the conflict. So, get at readme and then get stash. But, everything is gone now. So, if I look at the status, I can do stuff as if I never changed anything and then I can say get stash pop to get it back whenever I need it. And now there's a final thing I want to show you, which is a gitignore file. This is actually very foundational, fundamental. Sometimes I have files that I want to ignore. Typical thing would be a node modules directory if you're working with a node js project. A typical thing would be cache files. So, let's say I have some some file.cache even though .cache is not really the ending, but let's say this is some cache file I don't want to track. Sometimes I might have .environment files with secret API keys. This is all stuff that I don't necessarily want to commit. So, if I do get status, these are also listed at as hey, you maybe want to add these. And if I do something like get at everything, these are also going to be considered. And of course, let's go into node modules and create some stuff here. Like this. Then I would also get this. So, if I say get status, all these are now relevant. If I want to ignore them because they're not relevant for pushing them onto get, I can create a file called .gitignore. And in here I can just put directories or patterns. I can say here for example node modules or I can say everything so asterisk.cache or I can say dot env. And if I do that, if I now do get status, you will see that these are no longer shown. We only see the git ignore file. So if even if I say get at everything, you will see that I get a warning message that these are not actually used because they're ignored by my git ignore file. But if I say get status, these are not tracked. I can track them if I want to. I can say dash where is it dash f if I really want to. But other than that, I can just add stuff and these are never shown as changed. So that is the purpose of a git ignore file, very useful. So that's basically it. This was quite comprehensive. We covered a lot in a short amount of time so it was also very dense. I hope it was not too much. Maybe you want to watch this multiple times. Maybe you want to watch some other crash courses or consult some other resources in addition to that. I really tried to optimize for speed here. So I hope you like it. I hope you learned something. If so, let me know by hitting a like button and leave a comment in the comment section down below. Also, if you're interested on my website, you will find a services tab and a tutoring tab. There you can contact me if you need help with a project, if you need a freelancer, consultant, teacher, whatever. You can contact me via LinkedIn or email at the bottom of these pages. Besides that, don't forget to subscribe to this channel and hit the notification bell to not miss a single future video for free. Other than that, thank you much for watching. See you in the next video and bye.
Original Description
💻️ Need some help with a project or some consulting? Contact me here: https://www.neuralnine.com/services
🐍 The Python Bible Book: https://www.neuralnine.com/books/
💻 The Algorithm Bible Book: https://www.neuralnine.com/books/
Timestamps:
(0:00) Intro
(0:58) Basic Idea of Git
(6:46) Create GitHub Repository
(11:33) git add, commit, push, status
(14:53) git config
(16:01) git restore
(18:59) git diff
(19:24) git pull, fetch, remote
(23:22) git log
(23:40) git branch, switch
(33:21) git merge
(38:45) git reset
(41:28) git rebase
(48:18) git stash
(50:42) gitignore File
(52:30) Outro
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
More on: ML Pipelines
View skill →
🎓
Tutor Explanation
DeepCamp AI