9:35 you are really right here, the bit of information about "how they were once" is really important. Considering this I don't understand why most 3-way merge editor (integrated in git GUIs) ignore that and just show current branch/result/merging branch. Beyond compare, which I was using with SVN, instead show you current branch/common base/merging branch, and the result in a view below. For trivial mergers this is not really necessary, but I find it fundamental when it's up to trickier ones.
The premise with visualization was really good, but I have to say this feels more like a quick whirlwind tour for people who are already well-versed with the subject matter than a tutorial for people who aren't.
As a reviewer, a cleaned and rebased set of commits is much easier to review, and is easier to understand when investigating history. As long as your feature branch doesn't have a ton of churn, the conflicts you get when rebasing should be roughly equivalent to those when merging. If it does have a lot of churn, it likely needs some clean-up (the kind of things interactive rebase can help with: splitting, squashing, re-ordering).
The main selling point for me that converted me to a rebaser, is that I need to review my invidual commits as atoms (while developing and while pushing), not only external reviewers. Ah and making friends of reflog history makes you less scared to delete commits
Thank you very much. I have seen many many GIT tutorials but this is I think one of the best! I will recommend it to my students (still) struggling with GIT!
Man i spent so much time trying to re-find this damn video! I watched it for the first two years ago when i learned git and though to myself "nah, too complicated, i'll keep it in mind for later". Well, the day has come. And oh man did i search for this video. This. This one. A true life saver. Thanks.
Wanted to say, I use "git add -i". Interactive mode is super useful to show which files have been modified, and then to select if you want to patch them, individually or add untracked files all in a fairly easy to use interface.
It's just `git add -p` with extra steps but it's sometimes useful when you have a lot of files you don't want to add. The plus of the flag `-p` is that it also works with `git restore`, while `-i` does not.
@@eloniusz I don't see how it's incompatible with git restore. It's doing the same operations as git add -p is under the hood. I agree it can be more steps, but it also means you can select the specific files you want rather than having to go through every file if you have some files that have been modified that you don't want to add, it also gives the option of adding new files in an easy way. Edit: a word
The thing about rebase I like the most, is that it keeps your history linear and doesn't create additional merge commits. In many cases I pull quite often, so I don't want to create extra merge commit without reason, I just do git pull origin --rebase. Of course, if you your branch diverged a lot from main it's safer to do a merge
One hint for any git user: if you are doing something complex with git say to repo a (which is a full directory), doing "cp -r a b", ie., making a full copy of the entire repo, can really save you. Git does nothing until you commit and push. You can always go back to the previous copy of the repo and start again. Further, if you get used to working on the tree level, even with big trees, life can be good. For example "diff -r a b" (compare two entire directory trees), or better yet "meld a b" give you complete comparisions of repos. Finally, learn to manipulate patch files. Git is based on patch files, and they have existed longer than git. Git is basically an automated patch file program. Learning how patch files work is like learning the assembly language of git.
@@jzm2k I think you missed the point. The idea is if you are unsure what GIT does, and want an ultimate safe fallback, you can make a copy and then start again.
@@scottfranco1962 Maybe I'm focusing on an insignificant part of your message but you said *any git user*. If you're a beginner and don't know that reflog exists, I understand making a copy of the directory but after you've discovered such a tool exists in git, there is no need to make a copy of the directory any more because git gives you the tools to undo your mistakes. It's literally a tool for recording the history of your project so the same way as you can add new entries to the log, you can modify and delete old entries.
@@jzm2k But to be fair to a beginner like me Scott is absolutely right. Will save you the paranoia and allow you to try stuff when you arent comfortable with the full set of commands.
A commit is *not* a diff, but rather a complete snapshot of the working directory at the point in time the commit was created. Some git commands (like cherry-pick) indeed *treat* commits as diffs, but those commands must compute those diffs on the fly; they are not stored anywhere longterm!
@@archeryo5218 Yes, but files and directories with the same contents between commits are only stored once in gits database as "blobs" and "trees". (And git does internal delta compression from time to time, but that does not directly correspond to diffs between versions.) Here is how you can see the entire snapshot of e.g. the current master commit: $ git cat-file -p master tree 1234567... $ git cat-file -p 1234567 Replace 1234567 with the actual shown tree hash on your machine.
Yes “diff” is pulling a lot of weight in my description… it would be more accurate to say that git stores the history very efficiently by looking at the differences between commits. It isn’t literally storing text diffs, but as a mental model that still works pretty well for understanding how it works.
A commit can mean both - a complete snapshot or a diff with a previous snapshot. Either one can be deduced from the other, and how git exactly stores it is an implementation detail. You can use commits as snapshots or diffs depending on your needs. For "git show", you can think of commits as snapshots. For "git cherry-pick", it will only apply the differences, so you can think of it as diffs.
@@danielwilkowski5899 If commits were diffs, all git tools (git Bash, IntelliJ IDEA, GitHub...) would display the exact same diff for a given commit; which they don't. So it's definitely not just an implementation detail.
This is good one on the all concept of Git. But things I want to add is about cherry-pick. General use case for cherry-pick is to maintain the old version or the release version of the software/branch. Let's say you maintaining version 1.0.3 for old system while current release is now 2.x. So it not ever gonna get merge. So you have bugfix in 2.x and you want to use that commit on 1.x also, that when you actually do the Cherry-pick and tag it as 1.0.4. Another case is maintain the production release. I also use cherry-pick a lot to apply hotfix to the production release and then when the testing on new release is done we can do merge again. And usually with cherry-pick I do alter some commit messages to say that this is hotfix. so when the actual fix may get merge later log history will not look too obvious.
If you don't want to stage an entire hunk with 'git add -p' and just want to stage a few lines, you can use 'git add -e' to pick and choose which lines you want to stage without using a separate tool like git-cola. Also, if you want to write a commit message with a body, you can use 'git commit -e' to type in a title in the first line followed by an empty line then write your commit message body on the third line.
I am a bit confused about the meaning of the arrows. I thought the direction of the arrow was to be seen as some kind of direction of "time", that is more things are added in the directions of the arrows? The exact point I get lost is at 7:49 where you branch off. What is the meaning of the arrow pointing from the hotfix, branch to the main branch? The hotfix branch surely is not development history for the the white dot it is pointing to on the main branch? Especially not when further commits are made on the hotfix branch. Also, I thought the head always pointed to the most recent commit you made? At 8:08 it seems that it instead points at the start of your branch and not the lastest commit to the left of it? Or do I interpret the arrows in the inverse way, where arrows point backwards in "time"? I guess this is how it is. It would make sense from the rest. Edit: Ah, I guess the way to think about them is as pointers. The latest comit points to the previous and so on. This video seems to be meant for me. I have used Git in many years without really understanding much what is really happening..
the arrows illustrate "reachability". if you start from any commit and follow the arrows backwards, you can build a graph of all the commits which are "reachable" from that commit; in other words, all the "previous" commits (not necessarily chronologically earlier) which are relevant to the commit you are interested in. in general, with git, you start at the present (aka HEAD) and work backwards, everything is relative to the HEAD commit.
Really nice tutorial. Thanks a lot. Concerning the rebase command it deserves a bit more love. I use it all the time but not as an alternative to merge. I rebase in interactive mode my local history before I merge to the main branch. By doing that you can get a clean history by squashing commits together to simplify the history, or re-order them. It’s way easier for the reviewer to accept your PR later if everything looks good and logical.
@@saaah707 The amount of sheer pointless noise generated when you don't rebase is asking for way more trouble than the risk of introducing issues during the rebase, I find. But according to our omnipotent host, we just have bad mindsets...
Love video, similarly I would add that rebase helps with large project with linear commits logs instead of merge with two heads. Imagine if you are merging and resolving conflict your code with result of another merge conflict * n. Suddenly you are reviewing the whole code base. Where as rebase isolate the change to only your changes for the next developer. Overall, rebase is a little more complex, but you can just stash your changes. Worse scenario, you resolve conflict on an unstash and it is no different from merge conflict, but you make life easier for reviewer and other developers.
Supporting multiple branches and realizing you used a more recent commit than you intended as the base when creating your branch is the main reason why I use rebase. That, and also interactive rebase with commit re-ordering and squashing, but that's just admitting I was naughty when I initially committed!
Nice tut. Concerning rebase it is way simpler. You are safe to rebase unless you pushed your branch. Once you did, you should never rebase again. Anytime, anywhere. Most IDE even warns you. Cant tell if git itself warns or not as I am not a CLI master. I would explain its function a little bit different than the way you did. It will shift the whole branch with all your commits and keeps the branch. Only the "junction" will move ahead.
There is a workflow where rebasing is useful. Imagine you are working on a repository that has a CI/CD pipeline set up with automated tests. In order to merge your code to a protected branch, the pipeline for your branch must succeed otherwise the Git platform blocks the merge. This is a logical setup since the pipeline for a develop branch must pass before your branch can be merged into master. However, what if master has changed since you branched off some time ago in such a way that your develop branch would result in bugs after being merged in? The current setup does not protect against this case since the pipeline has only passed for develop which may have been forked from on an older commit in master. What you can do is also configure your repo such that only fast-forward merges into the protected branch are allowed. If the branch is forked from an old commit in master, the developer has to rebase their branch onto the lastest master then force push and if the pipeline succeeds then you know nothing is broken (assuming your automated tests are off a good quality) and you can fast forward merge free from any conflicts or (merge-related) bugs. Merging the master branch into your develop branch at regular intervals to keep it up to date may work theoretically but its a manual approach where you rely on the developer remembering to keep their branches up to date. The approach using rebase I outlined above will catch any such mistakes and give developers the confidence that a merge will not cause bugs.
Another video taught me that git does not in fact use diffs, but stores entire files, and commits contains references to files in the form of hashes, such that unchanged files aren't stored multiple times unnecessarily. That's more easy to reason about than diffs. Not sure if I can trust the rest of your lecture!
I’m not attempting to describe the internal implementation. The point is that it is largely irrelevant. Developing the mental model of how commands interact with the commit graph is much more important.
speak for yourself, keeping a separate mental tree for each file is much more cumbersome than the usual explanation that each commit stores the difference between itself and the previous commit.
@@tissuepaper9962 Don't understand your point of view. To me knowing that a commit represents the state of my code at a specific moment is more straightforward than the difference between each state.
@@billgrant7262 then you aren't really engaging with the idea of the commit graph. all the agorithms git uses to walk the commit graph work conceptually with differences, not with complete snapshots of the codebase. if you only conceive of the commit as the complete snapshot, then merge, rebase, revert, cherry-pick, etc. don't make much conceptual sense. you can't do any of those without first calculating the differences between all the commits and their parents, so conceptually it's simpler to flip it around and say that the commit *is* just the difference between itself and its parent commit, and a complete snapshot of the code is constructed by walking back through the working tree and applying all the reachable commits.
Rebasing helps keep a much cleaner history, especially if you are working in a bigger team. It can quickly get messy with all the merges. There is definitely a cost of doing rebases, but I find that it's many times worth it in the long run.
@@floatinglittleleavesofcode6473 it does matter because it gets easier to work with. Much like clean code has value. Perhaps not everyone on the team is a “git expert”. I’ve experienced both clean and not so clean history and I would prefer the clean one every time.
@@mrbigsmile3902 Not OP, but the main cost for me is accidentally destroying commits. If you aren’t careful, it is not hard to make a mistake while rebasing and then the original commits can be truly lost forever. This is not possible with merges.
great tutorial!!! has been using git for long time but never understood all the internal concepts.... there lot of tutorial for beginners, but nothing much for intermediate followed entire video , was interesting through out
The reason why git bisect takes so little to work with is because it is a binary search which just means that removes half of the remaining commits on each step of the process
What a great video!!! Thank you. At 35:43 you were talking about the ~/.ssh/config file; I understand the IndentityFile but where do we get the user, hostname and port? Anybody know? Thanks again for the great video!!!
Those are usually mentioned in the git server’s settings. For example the github help page for SSH says to use user git, host ssh.github.com, and port 443.
"It is immutable". Yeah, it is... an interesting statement. It isn't just related, though. It is a hash of the exact content. Commits are not diffs in git. It never treats them as diffs. Each file is always a complete version that is hashed. Any given content is only ever stored one time. So if you make a change and do a commit, then you make a change that makes it the same as it was before... the old version and the latest version that are identical will be pointed to as the same file in the git metadata.
You can think of it like that but in the long run git does store diffs through compression in order to avoid wasting too much storage, you can trigger the compression using `git gc`, but it's not useful at all to think about commits at that level of abstraction as it's more of a storage problem then a version control problem
It's true that the commit SHA is an immutable checksum for the complete state of your code at that point and its place in the chain leading up to there. But the commit _is_ conceptualized as a diff. That's why you can cherry-pick a commit and it means apply that diff.
Thank you so much. I was tought using git with source tree, but really, all these buttons without eyplanation are very confusing. I will from now on just use the shell and be happy :D
I've never been working in a way such that there may be multiple persons commiting on a same branch, in this situation the git fetch operation makes sense because you don't necessarily want to resync with the remote branch right away. In a workflow where 1 feature branch = 1 active person, you would just fast-forward merge the develop branch with origin by using the git pull operation then eventually do a 3-way merge with your feature branch. git format-patch and git am are the enhanced git stash and git stash pop except it takes commits themselves not just their content (you can save the last three commits, then reapply the three commits in a row later on). ex: git format-patch abc123^..HEAD --stdout > tmp.patch git reset --hard abc123~1 git am tmp.patch # solve potential conflicts with git am --show-current-patch, then git add, then git am --continue Reasons you would want to use merge instead of rebase to resync your feature branch with its parent: - We can see in the feature branch commit history when we resynced the feature branch with develop, and also see others commits along with ours in the same timeline - Even after pusing, we can still revert the merge (via a revert commit or with a reset --hard + push -f) - We preserve commits original intention (modifications that were applied to a specific version of the product, when we hadn't synced with the develop since n days, etc..) - We can troubleshoot bugs with git bisect (which wouldn't make sense after a rebase since new commits are inserted at the beginning of our branch's, so if the cause was from a resync with develop you would never know)
Usually the object next to the pointy arrow is the current (modified state) and the object at the tail of the arrow is the previous state. You changed their orientation and it took me a while to understand three-way merge. Yes I'm frustrated, confused, and uncomfortable with git. 🙂 But nonetheless, this tutorial is great.
That's a handy one. My usual workflow is to git add -p and repeat that until git diff only shows irrelevant changes that I don't care about, but I can see how --cached would be good for crafting larger commits that are harder to keep track of.
I do wonder if there is a way to dynamically generate fork graphs (perhaps based on GitHub actions), to make a clean a presentable version history readily showable to other Devs.
I expect there is, but github’s forking model is somewhat specific to github. Git itself does not prescribe any kind of forking model; it allows any repo to have any number of remotes. So look for github-specific tools if you are specifically interested in forks.
It's not a talk about the basics of _using_ git. I can imagine someone using git at a rudimentary level without knowing any of this stuff, just by memorizing workflows in a git GUI that someone showed them how to use. Understanding the basic principles behind git is incredibly important, and is very much in scope. Knowing how a commit is defined is something a lot of noobies miss!
Great tutorial, can you talk more about during merge git takes a decision (commit) if there are conflicts, based on what we choose. How do we go back on those decisions? As once you resolve merge conflict, git ignores that part of code which is different between file A and file B. But what if I want to revert my merge from few commits ago, a graph of how that looks like would be super helpful. Thanks!
A merge commit is not so different from any other. It still only exists locally until you push and is thus easy to modify after the fact. There are many ways to do that: revert, --amend, or just checking out an older commit and trying the merge again.
@@floatinglittleleavesofcode6473 merging can be messy because it doesn't preserve a linear commit history. The difference between merge and rebase is one preserves a linear history (rebase) and one does not (merge). Merging adds each commit chronologically, sort of shuffling them together where they are appropriate, and resolves any conflicts using a merge commit which dictates how the two should be merged together. Using a merge commit is nice because you only have to resolve conflicts once unlike rebase where you have to resolve conflicts for every commit that is reapplied. This can be annoying anytime I'm reading I take the opportunity to clean my commit history up using rebase interactive because sometimes us devs are messy. Since merging pollutes the history it can make it difficult to modify the commit history in the future if the target branch is very active. It also is more difficult to identify possible points of regression since the intent between the branches is lost due to a non linear history. On a feature branch I don't think it matters much which you prefer. What is more important is when you go to merge which merge/PR strategy you choose. For my organization we use squash and merge into the development branch which ensures a clean history and a fast-forward merge is always used since it's only a single commit with linear history. We use rebase and merge only if we want to preserve the commits within the PR which is rare. Sometimes it's done to preserve multiple different features as a part of the same PR in distinct commits. Finally, merge commit is only used when merging from develop to master to ensure master always stays ahead of development at the point of merge (all of the development commits plus the explicit merge commits.) Using rebase would cause the histories to diverge and squash would lose the commit history. I use rebase because it keeps my commit history linear which allows it to tell a story through time which is useful if you ever have to revert to a previous state. While developing most devs want to make several commits as landmarks throughout their development process. This is where rebase shines because it keeps those landmarks in the same place relative to each other after merging code and even allows you to clean them up in the process. But merging is easier, although loses out on the above advantages, if done regularly.
TH-cam started recommending me all sorts of random stuff lately and this time I clicked for the title thinking it was a video about 40k's Orks and how to be a git.
Thanks for the tutorial. A question: I ran into a problem of headless state after I checked out a specific commit inside the branch to make a pr on top of the specific commit. My manager told me don't checkout commit, alway checkout branch. I just don't understand this, what's the thing with headless state, and why it's a bad practice to checkout a commit?
The default diff view is not one of git's strong suits, but it is configurable! If you have a nicer diff tool, you can set up your git config to open it in a similar way to setting up a custom merge tool.
You said that you would merge origin/develop into feature every now and then, but sometimes there are conflicts and git tries to resolve them with a new commit which i don't like, usually i do git merge --ff-only which forces it to only do the merge only if a fast forward is possible if not it will fail in that case i would use git rebase to cleanly integrate the remote changes without adding an additional useless commit message, remeber these merge commits will build up overtime and the git history will be very ugly to look at.
"the git history will be very ugly to look at" In my experience, this is only a "problem" with your mindset. If you have a large team with multiple developers working separately and merging their code separately, the history *should* look complicated to reflect the reality of your workflow. Forcing yourself to use rebases lies about how the development occurred, and if you screw up the rebase (which is extremely common in my experience) you risk truly losing work, which defeats the point of using git.
@@floatinglittleleavesofcode6473 That depends a lot on the workflow and project. I work on GDB, so we use email for submitting patches, and when something is accepted it is added to master and pushed. There is no point in remembering the branch of a commit since each branch (or in our case patch series) is submitted separately and each commit should have enough information on its own to justify the change (or mention a future commit that will use it). This way, when bisecting for instance, we dont ever need to worry about merge commits and if git's conflict resolution process messed up somewhere. Now if you do it using branches and rebases, or if you checkout to master and apply the diffs, that is up to each developer, and having some - but not all - merge commits would make the history more confusing than if none were there.
The slides titles are the commands but I recommend using git help for more info. The talk would be twice as long if I tried to go into all of the specific options and arguments for each command.
Definitely on team rebase / push often / trunk based, stay current and don’t diverge from the one source of truth. Got onto this team after working in slow moving Gerrit-review horror gits from barely working orgs that post code freezes introduce an unreadable history with merges upon mergers where mental strain of trying to understand what happened is unacceptable. If the project is simple enough I’d prefer just banning merges and set the remote to fast forward only. Forces team members who want to merge to get their things working and easily understandable locally. Work from head not a feature branch, rebase and push often. For the projects that are able to work on trunk/main/master and not diverge into feature branches / merges / … it makes life in git and cooperation easier.
Most of those problems are solved by having a good CI system, specifically one that is PR-aware so it can do things like proactively notifying the author about conflicts with the target branch, merging the target branch before running tests, stuff like that. Frequent rebasing works around the issue by putting more burden on the developer, but if you can afford the infrastructure investment I think it's worth allowing people to have more options in their workflow.
@@MMABeijing it doesn't make sense to have arrows point from the past to the present because your are almost ALWAYS on HEAD (the present), that's your starting point, since you are always starting from the present (HEAD) it make sense to always point back to the past. It's just a better data structure for a snapshot based version control system such as git
@@goawqebt6931 I have accepted the notation and yes it reflects the data structure ( as only child nodes know about the address of the parent node) it is not helping understanding of git. I will just say that I totally understand the initial comment and add that git is poorly explained in general.
Yes, it is unintuitive at first. But the arrow direction is important! Git uses it to answer important questions like “what did you change?” and “Is this safe to push?” In general it is much harder to go against the arrows, so you’ll have an easier time if you can check out a later commit and look backwards than vice versa.
the most recent commit references the previous commit, not the other way around. it wouldn't make sense for the arrows to point "forward", how can you make a reference to part of the development history that doesn't exist yet?
It's hard to pick a specific one. I recommend first finding an open source project on github that you currently use. Then clone it and make a local branch with some modification that seems useful. If that goes well, fork the repo and add it as a new remote for your clone and push your branch, then open a PR.
I pretty much exclusively use rebase instead of merging 😅 I just like how simple it keeps my history, merge commits really bug me. I feel like it also forces me to deal with conflicts sooner rather than later.
That's also a valid workflow, but as an exercise try a merge-only workflow some time. I think it forces you to come to grips with git's more advanced features.
I've not truely understood the thing with submodules and why it's useful (i never had to use this nor have seen it), but otherwise it's a very useful tutorial
say you want to use two different versions of the same repository in two of your projects. instead of manually downloading each version and keeping them in separate directories like in the olden days, with submodules you can keep those two copies neat and tidy in their respective repositories. if you tried to just clone the submodules into the two project repos, git freaks out about multiple trees.
This seems like a simple complaint when I type it out, but it always gives me trouble understanding git graphs intuitively: The arrows are pointing backwards! I get that it's because it's changes piled on changes, what we're really looking at is a history. But I hate it 😭
It really helps to flip around the way you think about history in git. Instead of "This is what the code used to be. What happened next?" think about it as "This is the code now. What did it look like before?" Almost every git command approaches history in this way, so it's a good exercise to try it out yourself.
Solid tutorial, definitely a good step to become an expert, but I wouldn't necessarily say that it's all it takes to be an expert in git. For first, it doesn't mention line endings, file permissions, storing binary data, LFS, multiple remotes, fetch --prune, rebase -rerere, !fixup, multiple types of checkout (like --cached), rebase --onto, it doesn't say about octopus merge, multiple roots, push --force-with-lease, files on case-insensitive system (like windows), rebase --exec, cloning via https vs ssh, probably more. Additionally, to be an expert in git, it doesn't just take to know the tools; but more importantly - to be an expert you must *learn* the mindset, what are the ways to achieve some goal. Tools and commands only aid in that.
I agree. This video is mainly about how to start learning the mindset. Once someone has a reasonably accurate picture of how all of these basic commands work, they should be able to figure out the other things you mention on their own.
I now undestand why I always say to Git to Fork off... Now that I know that git will Fork off automatically I will be less frustrated... But I will keep saying to git to Fork off... just for fun!
Many git commands can take a file after them. git log PATH_TO_FILE If you want to see the actual code changes, you can use git log -p PATH_TO_FILE which will output the commits as a list of patches.
Good theoretical overview. Still some parts were vague. For example the part about rebasing. And about second kind of merging in git (where git is much smarter - 11:11 timecode). Otherwise thanks
I'm not sure what you found vague about those explanations so it's hard to address your confusion. But I'll try again. A fast-forward merge happens if and only if your current branch is reachable in the history of the branch your are merging. Then the merge simply updates the branch pointer and no new commits are created. Rebasing takes a set of commits and creates a brand new set of commits that tries to mimic the original set, possibly applying those commits on a different starting commit. It is hard to be more specific than that because rebasing has a *ton* of options. You can skip some of the original commits, combine some of them, edit some while they are being recreated, etc. There is literally no way to predict what the result of a rebase will be.
`git rebase --interactive` (or just -i) lets you rebase via a little wizard, instead of having to commit and continue constantly. Do read up on it a little, as you need to specify which "commands" should apply to which commits. The free Pro Git book has a chapter on it. URL in the next comment, because I don't know if urls are filtered.
i have a question. If you upload a long image to github, but later you want to delete that image. Is your repository wasting a lot of memory for storing that deleted image? i think you called, 'hard reset'
Yes, as long as there are commits in the history with that image present, the repository is going to contain that image, so if the image is 100Mb, then the repository is going to be 100Mb larger. The only way to get rid of it is to alter the git history and remove the image from each commit.
It doesn't ignore the files - if you change permissions on a file, git will correctly mark it as unstaged change, and you can either reset the change or "git add" and "git commit" it. It can be disabled in config, to ignore file permissions, but git stores them by default.
It is an inherent limitation. All directories in a git repo must be rwx or else git can’t operate on them. Permissions like user/group access cannot exist in the repo because there is no guarantee that they will exist on the machine where the repo is cloned. For example, all of the permissions that github manages regarding repo access, edit permissions, etc are all external to git itself.
The difference is like night and day, far too much to go into in a comment. Suffice to say CVS can at best poorly approximate a fraction of git’s power.
It depends on your commit style. Because git is so efficient at internally representing its commits, it encourages you to make many small commits on each branch rather than a small number of large commits. Many people find this style useful because it gives you more control over things like merging, reverting, etc.
Great tutorial but would be better named "git intermediate". An expert is going to know a lot more workflows and will know about things like tags, repo pruning, hooks, and ways to think about where you might find a merge conflict not detected as one.
The default (vimdiff) is the only one I know of that does it out of the box. But technically any editor that can do a four window split can be hooked up to git. The mergetool command can be customized to pass its four arguments to the tool of your choice. I once set up Sublime to do this for someone.
9:35 you are really right here, the bit of information about "how they were once" is really important. Considering this I don't understand why most 3-way merge editor (integrated in git GUIs) ignore that and just show current branch/result/merging branch. Beyond compare, which I was using with SVN, instead show you current branch/common base/merging branch, and the result in a view below. For trivial mergers this is not really necessary, but I find it fundamental when it's up to trickier ones.
You actually said the same thing at 37:45 😂
The premise with visualization was really good, but I have to say this feels more like a quick whirlwind tour for people who are already well-versed with the subject matter than a tutorial for people who aren't.
it is intended to be a conceptual deep dive, more so than a beginners tutorial. it's a feature not a bug.
Proud member of team rebase and I like the clean git history. Once you understand rebasing, it's not so scary.
As a reviewer, a cleaned and rebased set of commits is much easier to review, and is easier to understand when investigating history. As long as your feature branch doesn't have a ton of churn, the conflicts you get when rebasing should be roughly equivalent to those when merging. If it does have a lot of churn, it likely needs some clean-up (the kind of things interactive rebase can help with: splitting, squashing, re-ordering).
The main selling point for me that converted me to a rebaser, is that I need to review my invidual commits as atoms (while developing and while pushing), not only external reviewers.
Ah and making friends of reflog history makes you less scared to delete commits
what an impressive tutorial, I've been looking for content like this for months
Thank you very much. I have seen many many GIT tutorials but this is I think one of the best! I will recommend it to my students (still) struggling with GIT!
Man i spent so much time trying to re-find this damn video! I watched it for the first two years ago when i learned git and though to myself "nah, too complicated, i'll keep it in mind for later". Well, the day has come. And oh man did i search for this video. This. This one. A true life saver. Thanks.
did it also pay for your kids' college fees?
This was actually just as good if you don't know Git at all! Thank you!
This was the plain english git sanity check ive been looking for for a long time, thanks. Just subscribed.
Wanted to say, I use "git add -i". Interactive mode is super useful to show which files have been modified, and then to select if you want to patch them, individually or add untracked files all in a fairly easy to use interface.
TIL! I usually use add -p which is specifically for adding/editing changes, but -i is really neat!
It's just `git add -p` with extra steps but it's sometimes useful when you have a lot of files you don't want to add.
The plus of the flag `-p` is that it also works with `git restore`, while `-i` does not.
@@eloniusz I don't see how it's incompatible with git restore. It's doing the same operations as git add -p is under the hood. I agree it can be more steps, but it also means you can select the specific files you want rather than having to go through every file if you have some files that have been modified that you don't want to add, it also gives the option of adding new files in an easy way. Edit: a word
Great git walkthrough. No fluff, good pace and clear explanations with visuals.
The information in this video is like... NON-STOP! Constant knowledge. Had to pause it multiple times to just let it sink in.
LOVE IT!!! Thank you
The best git tutorial I've seen so far, thank you. EDIT: sound volume little to low, imo
The thing about rebase I like the most, is that it keeps your history linear and doesn't create additional merge commits. In many cases I pull quite often, so I don't want to create extra merge commit without reason, I just do git pull origin --rebase. Of course, if you your branch diverged a lot from main it's safer to do a merge
One hint for any git user: if you are doing something complex with git say to repo a (which is a full directory), doing "cp -r a b", ie., making a full copy of the entire repo, can really save you. Git does nothing until you commit and push. You can always go back to the previous copy of the repo and start again. Further, if you get used to working on the tree level, even with big trees, life can be good. For example "diff -r a b" (compare two entire directory trees), or better yet "meld a b" give you complete comparisions of repos. Finally, learn to manipulate patch files. Git is based on patch files, and they have existed longer than git. Git is basically an automated patch file program. Learning how patch files work is like learning the assembly language of git.
You don't need to make a copy of the entire directory. If you fuck up something, git-reflog is there to save you
@@jzm2k I think you missed the point. The idea is if you are unsure what GIT does, and want an ultimate safe fallback, you can make a copy and then start again.
@@scottfranco1962 Maybe I'm focusing on an insignificant part of your message but you said *any git user*. If you're a beginner and don't know that reflog exists, I understand making a copy of the directory but after you've discovered such a tool exists in git, there is no need to make a copy of the directory any more because git gives you the tools to undo your mistakes. It's literally a tool for recording the history of your project so the same way as you can add new entries to the log, you can modify and delete old entries.
@@jzm2k But to be fair to a beginner like me Scott is absolutely right. Will save you the paranoia and allow you to try stuff when you arent comfortable with the full set of commands.
Having multiple clones of the repo is another way. That is also the starting point of a git user that intends to work on an existing code repo.
A commit is *not* a diff, but rather a complete snapshot of the working directory at the point in time the commit was created. Some git commands (like cherry-pick) indeed *treat* commits as diffs, but those commands must compute those diffs on the fly; they are not stored anywhere longterm!
Does each commit store everything in each file?
@@archeryo5218 Yes, but files and directories with the same contents between commits are only stored once in gits database as "blobs" and "trees".
(And git does internal delta compression from time to time, but that does not directly correspond to diffs between versions.)
Here is how you can see the entire snapshot of e.g. the current master commit:
$ git cat-file -p master
tree 1234567...
$ git cat-file -p 1234567
Replace 1234567 with the actual shown tree hash on your machine.
Yes “diff” is pulling a lot of weight in my description… it would be more accurate to say that git stores the history very efficiently by looking at the differences between commits. It isn’t literally storing text diffs, but as a mental model that still works pretty well for understanding how it works.
A commit can mean both - a complete snapshot or a diff with a previous snapshot. Either one can be deduced from the other, and how git exactly stores it is an implementation detail. You can use commits as snapshots or diffs depending on your needs. For "git show", you can think of commits as snapshots. For "git cherry-pick", it will only apply the differences, so you can think of it as diffs.
@@danielwilkowski5899 If commits were diffs, all git tools (git Bash, IntelliJ IDEA, GitHub...) would display the exact same diff for a given commit; which they don't. So it's definitely not just an implementation detail.
This is good one on the all concept of Git.
But things I want to add is about cherry-pick. General use case for cherry-pick is to maintain the old version or the release version of the software/branch.
Let's say you maintaining version 1.0.3 for old system while current release is now 2.x. So it not ever gonna get merge. So you have bugfix in 2.x and you want to use that commit on 1.x also, that when you actually do the Cherry-pick and tag it as 1.0.4.
Another case is maintain the production release. I also use cherry-pick a lot to apply hotfix to the production release and then when the testing on new release is done we can do merge again. And usually with cherry-pick I do alter some commit messages to say that this is hotfix. so when the actual fix may get merge later log history will not look too obvious.
This short video about git is really helpful
This is the most helpful git tutorial I've watched, thank you for breaking it down into the basics so clearly.
If you don't want to stage an entire hunk with 'git add -p' and just want to stage a few lines, you can use 'git add -e' to pick and choose which lines you want to stage without using a separate tool like git-cola.
Also, if you want to write a commit message with a body, you can use 'git commit -e' to type in a title in the first line followed by an empty line then write your commit message body on the third line.
I am a bit confused about the meaning of the arrows. I thought the direction of the arrow was to be seen as some kind of direction of "time", that is more things are added in the directions of the arrows?
The exact point I get lost is at 7:49 where you branch off. What is the meaning of the arrow pointing from the hotfix, branch to the main branch?
The hotfix branch surely is not development history for the the white dot it is pointing to on the main branch? Especially not when further commits are made on the hotfix branch.
Also, I thought the head always pointed to the most recent commit you made? At 8:08 it seems that it instead points at the start of your branch and not the lastest commit to the left of it?
Or do I interpret the arrows in the inverse way, where arrows point backwards in "time"?
I guess this is how it is. It would make sense from the rest.
Edit: Ah, I guess the way to think about them is as pointers. The latest comit points to the previous and so on.
This video seems to be meant for me. I have used Git in many years without really understanding much what is really happening..
Arrow is pointing to the parent commit. (I think)
the arrows illustrate "reachability". if you start from any commit and follow the arrows backwards, you can build a graph of all the commits which are "reachable" from that commit; in other words, all the "previous" commits (not necessarily chronologically earlier) which are relevant to the commit you are interested in. in general, with git, you start at the present (aka HEAD) and work backwards, everything is relative to the HEAD commit.
Fantastic explanations and tips!
Great video thanks very much.
One feedback is try avoiding tapping the table, it hurts the sound quality.
Cheers
Thanks for the tip!
You had me at fetch/merge vs. pull. I'm going to try that.
pull is literally fetch and merge with tracking branch (or fetch and rebase, depending on configuration).
The best thing i have seen about git! Thank you so much! Now i can wear my git-fan shirt without feeling like a fraud 😊
Didn't know about bisect ! Great tips. Thanks
No problem!
Great presentation! Out of curiosity, what presentation/slide software do you use?
Really nice tutorial. Thanks a lot. Concerning the rebase command it deserves a bit more love. I use it all the time but not as an alternative to merge. I rebase in interactive mode my local history before I merge to the main branch. By doing that you can get a clean history by squashing commits together to simplify the history, or re-order them. It’s way easier for the reviewer to accept your PR later if everything looks good and logical.
This, once you actually experience a rebase workflow it's hard to go back
@@saaah707 The amount of sheer pointless noise generated when you don't rebase is asking for way more trouble than the risk of introducing issues during the rebase, I find. But according to our omnipotent host, we just have bad mindsets...
Love video, similarly I would add that rebase helps with large project with linear commits logs instead of merge with two heads. Imagine if you are merging and resolving conflict your code with result of another merge conflict * n. Suddenly you are reviewing the whole code base. Where as rebase isolate the change to only your changes for the next developer. Overall, rebase is a little more complex, but you can just stash your changes. Worse scenario, you resolve conflict on an unstash and it is no different from merge conflict, but you make life easier for reviewer and other developers.
Yes, that is my main use of rebase as well. Not to change the starting commit but just to combine messy commits before I push.
Supporting multiple branches and realizing you used a more recent commit than you intended as the base when creating your branch is the main reason why I use rebase. That, and also interactive rebase with commit re-ordering and squashing, but that's just admitting I was naughty when I initially committed!
Nice tut. Concerning rebase it is way simpler. You are safe to rebase unless you pushed your branch. Once you did, you should never rebase again. Anytime, anywhere. Most IDE even warns you. Cant tell if git itself warns or not as I am not a CLI master. I would explain its function a little bit different than the way you did. It will shift the whole branch with all your commits and keeps the branch. Only the "junction" will move ahead.
There is a workflow where rebasing is useful. Imagine you are working on a repository that has a CI/CD pipeline set up with automated tests. In order to merge your code to a protected branch, the pipeline for your branch must succeed otherwise the Git platform blocks the merge. This is a logical setup since the pipeline for a develop branch must pass before your branch can be merged into master.
However, what if master has changed since you branched off some time ago in such a way that your develop branch would result in bugs after being merged in? The current setup does not protect against this case since the pipeline has only passed for develop which may have been forked from on an older commit in master.
What you can do is also configure your repo such that only fast-forward merges into the protected branch are allowed. If the branch is forked from an old commit in master, the developer has to rebase their branch onto the lastest master then force push and if the pipeline succeeds then you know nothing is broken (assuming your automated tests are off a good quality) and you can fast forward merge free from any conflicts or (merge-related) bugs.
Merging the master branch into your develop branch at regular intervals to keep it up to date may work theoretically but its a manual approach where you rely on the developer remembering to keep their branches up to date. The approach using rebase I outlined above will catch any such mistakes and give developers the confidence that a merge will not cause bugs.
Another video taught me that git does not in fact use diffs, but stores entire files, and commits contains references to files in the form of hashes, such that unchanged files aren't stored multiple times unnecessarily. That's more easy to reason about than diffs. Not sure if I can trust the rest of your lecture!
I’m not attempting to describe the internal implementation. The point is that it is largely irrelevant. Developing the mental model of how commands interact with the commit graph is much more important.
@@floatinglittleleavesofcode6473 Fair enough
speak for yourself, keeping a separate mental tree for each file is much more cumbersome than the usual explanation that each commit stores the difference between itself and the previous commit.
@@tissuepaper9962 Don't understand your point of view. To me knowing that a commit represents the state of my code at a specific moment is more straightforward than the difference between each state.
@@billgrant7262 then you aren't really engaging with the idea of the commit graph. all the agorithms git uses to walk the commit graph work conceptually with differences, not with complete snapshots of the codebase. if you only conceive of the commit as the complete snapshot, then merge, rebase, revert, cherry-pick, etc. don't make much conceptual sense. you can't do any of those without first calculating the differences between all the commits and their parents, so conceptually it's simpler to flip it around and say that the commit *is* just the difference between itself and its parent commit, and a complete snapshot of the code is constructed by walking back through the working tree and applying all the reachable commits.
Great presentation tho, good coverage and clear explanation. Thanks for putting this together.
Very helpful video! If possible, could you please increase the volume a bit?
Great presentation! Very clear and concise with some nice graphs. Thank you for this!
Rebasing helps keep a much cleaner history, especially if you are working in a bigger team. It can quickly get messy with all the merges. There is definitely a cost of doing rebases, but I find that it's many times worth it in the long run.
Everyone also says "cleaner history" and I always say "who cares?" If you get good at using git log, it doesn't matter how "clean" the history is.
@@floatinglittleleavesofcode6473 it does matter because it gets easier to work with. Much like clean code has value. Perhaps not everyone on the team is a “git expert”.
I’ve experienced both clean and not so clean history and I would prefer the clean one every time.
Which costs of rebase did you have in mind?
@@mrbigsmile3902 Not OP, but the main cost for me is accidentally destroying commits. If you aren’t careful, it is not hard to make a mistake while rebasing and then the original commits can be truly lost forever. This is not possible with merges.
great tutorial!!! has been using git for long time but never understood all the internal concepts....
there lot of tutorial for beginners, but nothing much for intermediate
followed entire video , was interesting through out
The reason why git bisect takes so little to work with is because it is a binary search which just means that removes half of the remaining commits on each step of the process
That was a great tutorial. Thank you for sharing your knowledge!
just started watching but this is exactly the type of video I was looking for. thanks!
What a great video!!! Thank you. At 35:43 you were talking about the ~/.ssh/config file; I understand the IndentityFile but where do we get the user, hostname and port? Anybody know? Thanks again for the great video!!!
Those are usually mentioned in the git server’s settings. For example the github help page for SSH says to use user git, host ssh.github.com, and port 443.
Amazing talk! Thanks!
Thank you, watching this video was a good time investment
"It is immutable". Yeah, it is... an interesting statement. It isn't just related, though. It is a hash of the exact content.
Commits are not diffs in git. It never treats them as diffs. Each file is always a complete version that is hashed. Any given content is only ever stored one time. So if you make a change and do a commit, then you make a change that makes it the same as it was before... the old version and the latest version that are identical will be pointed to as the same file in the git metadata.
You can think of it like that but in the long run git does store diffs through compression in order to avoid wasting too much storage, you can trigger the compression using `git gc`, but it's not useful at all to think about commits at that level of abstraction as it's more of a storage problem then a version control problem
It's true that the commit SHA is an immutable checksum for the complete state of your code at that point and its place in the chain leading up to there. But the commit _is_ conceptualized as a diff. That's why you can cherry-pick a commit and it means apply that diff.
@@Sonsequence Mkay. Go let the man that is responsible for libgit2 (which runs GitHub, GitLab, etc) know.
Fantastic video, exactly what I needed.
Thank you so much. I was tought using git with source tree, but really, all these buttons without eyplanation are very confusing.
I will from now on just use the shell and be happy :D
I've never been working in a way such that there may be multiple persons commiting on a same branch, in this situation the git fetch operation makes sense because you don't necessarily want to resync with the remote branch right away. In a workflow where 1 feature branch = 1 active person, you would just fast-forward merge the develop branch with origin by using the git pull operation then eventually do a 3-way merge with your feature branch.
git format-patch and git am are the enhanced git stash and git stash pop except it takes commits themselves not just their content (you can save the last three commits, then reapply the three commits in a row later on).
ex:
git format-patch abc123^..HEAD --stdout > tmp.patch
git reset --hard abc123~1
git am tmp.patch
# solve potential conflicts with git am --show-current-patch, then git add, then git am --continue
Reasons you would want to use merge instead of rebase to resync your feature branch with its parent:
- We can see in the feature branch commit history when we resynced the feature branch with develop, and also see others commits along with ours in the same timeline
- Even after pusing, we can still revert the merge (via a revert commit or with a reset --hard + push -f)
- We preserve commits original intention (modifications that were applied to a specific version of the product, when we hadn't synced with the develop since n days, etc..)
- We can troubleshoot bugs with git bisect (which wouldn't make sense after a rebase since new commits are inserted at the beginning of our branch's, so if the cause was from a resync with develop you would never know)
Thanks for elaborating on all of these commands. Great reasons to favor merge over rebase!
Usually the object next to the pointy arrow is the current (modified state) and the object at the tail of the arrow is the previous state. You changed their orientation and it took me a while to understand three-way merge. Yes I'm frustrated, confused, and uncomfortable with git. 🙂 But nonetheless, this tutorial is great.
Realizing why the arrows are “backwards” is part of the trick of becoming an expert!
A big oversight I noticed: git diff --cached
It shows the changes that will be committed when you run git commit
Otherwise an amazing tutorial
That's a handy one. My usual workflow is to git add -p and repeat that until git diff only shows irrelevant changes that I don't care about, but I can see how --cached would be good for crafting larger commits that are harder to keep track of.
I do wonder if there is a way to dynamically generate fork graphs (perhaps based on GitHub actions), to make a clean a presentable version history readily showable to other Devs.
I expect there is, but github’s forking model is somewhat specific to github. Git itself does not prescribe any kind of forking model; it allows any repo to have any number of remotes. So look for github-specific tools if you are specifically interested in forks.
Great video! May I ask what software you are using for your slides?
Mac Keynote
super helpful and intuitive overview
Very helpful tutorial! Thanks a lot for making this!
Solid tutorial! Thank you!
6:30 I thought you said this wasnt a basic talk about git, when do we get to the non basic stuff?
It's not a talk about the basics of _using_ git. I can imagine someone using git at a rudimentary level without knowing any of this stuff, just by memorizing workflows in a git GUI that someone showed them how to use.
Understanding the basic principles behind git is incredibly important, and is very much in scope. Knowing how a commit is defined is something a lot of noobies miss!
Thank you so much for the video!!!
Great tutorial, can you talk more about during merge git takes a decision (commit) if there are conflicts, based on what we choose. How do we go back on those decisions?
As once you resolve merge conflict, git ignores that part of code which is different between file A and file B. But what if I want to revert my merge from few commits ago, a graph of how that looks like would be super helpful. Thanks!
A merge commit is not so different from any other. It still only exists locally until you push and is thus easy to modify after the fact. There are many ways to do that: revert, --amend, or just checking out an older commit and trying the merge again.
@@floatinglittleleavesofcode6473 merging can be messy because it doesn't preserve a linear commit history. The difference between merge and rebase is one preserves a linear history (rebase) and one does not (merge).
Merging adds each commit chronologically, sort of shuffling them together where they are appropriate, and resolves any conflicts using a merge commit which dictates how the two should be merged together.
Using a merge commit is nice because you only have to resolve conflicts once unlike rebase where you have to resolve conflicts for every commit that is reapplied. This can be annoying anytime I'm reading I take the opportunity to clean my commit history up using rebase interactive because sometimes us devs are messy.
Since merging pollutes the history it can make it difficult to modify the commit history in the future if the target branch is very active. It also is more difficult to identify possible points of regression since the intent between the branches is lost due to a non linear history.
On a feature branch I don't think it matters much which you prefer. What is more important is when you go to merge which merge/PR strategy you choose. For my organization we use squash and merge into the development branch which ensures a clean history and a fast-forward merge is always used since it's only a single commit with linear history. We use rebase and merge only if we want to preserve the commits within the PR which is rare. Sometimes it's done to preserve multiple different features as a part of the same PR in distinct commits. Finally, merge commit is only used when merging from develop to master to ensure master always stays ahead of development at the point of merge (all of the development commits plus the explicit merge commits.) Using rebase would cause the histories to diverge and squash would lose the commit history.
I use rebase because it keeps my commit history linear which allows it to tell a story through time which is useful if you ever have to revert to a previous state.
While developing most devs want to make several commits as landmarks throughout their development process. This is where rebase shines because it keeps those landmarks in the same place relative to each other after merging code and even allows you to clean them up in the process. But merging is easier, although loses out on the above advantages, if done regularly.
TH-cam started recommending me all sorts of random stuff lately and this time I clicked for the title thinking it was a video about 40k's Orks and how to be a git.
Orks do use git, but they always force push.
Very well done, many thanks!
this was awesome, thanks for sharing :)
Thanks for the tutorial.
A question: I ran into a problem of headless state after I checked out a specific commit inside the branch to make a pr on top of the specific commit.
My manager told me don't checkout commit, alway checkout branch.
I just don't understand this, what's the thing with headless state, and why it's a bad practice to checkout a commit?
Awesome video!
Thanks!
great explanation
This is gold. Thank you!😊
Any tips on how to get used to diff syntax? Comming from vscode highlighting, diff output is ... complex to say the least.
The default diff view is not one of git's strong suits, but it is configurable! If you have a nicer diff tool, you can set up your git config to open it in a similar way to setting up a custom merge tool.
You said that you would merge origin/develop into feature every now and then, but sometimes there are conflicts and git tries to resolve them with a new commit which i don't like, usually i do git merge --ff-only which forces it to only do the merge only if a fast forward is possible if not it will fail in that case i would use git rebase to cleanly integrate the remote changes without adding an additional useless commit message, remeber these merge commits will build up overtime and the git history will be very ugly to look at.
"the git history will be very ugly to look at"
In my experience, this is only a "problem" with your mindset. If you have a large team with multiple developers working separately and merging their code separately, the history *should* look complicated to reflect the reality of your workflow. Forcing yourself to use rebases lies about how the development occurred, and if you screw up the rebase (which is extremely common in my experience) you risk truly losing work, which defeats the point of using git.
@@floatinglittleleavesofcode6473 That depends a lot on the workflow and project. I work on GDB, so we use email for submitting patches, and when something is accepted it is added to master and pushed. There is no point in remembering the branch of a commit since each branch (or in our case patch series) is submitted separately and each commit should have enough information on its own to justify the change (or mention a future commit that will use it). This way, when bisecting for instance, we dont ever need to worry about merge commits and if git's conflict resolution process messed up somewhere.
Now if you do it using branches and rebases, or if you checkout to master and apply the diffs, that is up to each developer, and having some - but not all - merge commits would make the history more confusing than if none were there.
It'd be helpful to include commands along with the git branching graphics. Great video though!
The slides titles are the commands but I recommend using git help for more info. The talk would be twice as long if I tried to go into all of the specific options and arguments for each command.
the four points he mentioned under who this talk is for? I literally thought he was talking about me.
And now you're an expert! Hopefully...
Definitely on team rebase / push often / trunk based, stay current and don’t diverge from the one source of truth. Got onto this team after working in slow moving Gerrit-review horror gits from barely working orgs that post code freezes introduce an unreadable history with merges upon mergers where mental strain of trying to understand what happened is unacceptable. If the project is simple enough I’d prefer just banning merges and set the remote to fast forward only. Forces team members who want to merge to get their things working and easily understandable locally. Work from head not a feature branch, rebase and push often. For the projects that are able to work on trunk/main/master and not diverge into feature branches / merges / … it makes life in git and cooperation easier.
Most of those problems are solved by having a good CI system, specifically one that is PR-aware so it can do things like proactively notifying the author about conflicts with the target branch, merging the target branch before running tests, stuff like that. Frequent rebasing works around the issue by putting more burden on the developer, but if you can afford the infrastructure investment I think it's worth allowing people to have more options in their workflow.
Are the arrows pointing that way because they refer back to previous states? Freaks me out as they are chronologically the opposite?
It's not just how decided to visualize them, that's in fact how git work, the commit nodes refer back to their parents not the other way around
but that s nnot.helping the ee xplanation at all. it would make.sense to have the arrow point the other way
@@MMABeijing it doesn't make sense to have arrows point from the past to the present because your are almost ALWAYS on HEAD (the present), that's your starting point, since you are always starting from the present (HEAD) it make sense to always point back to the past. It's just a better data structure for a snapshot based version control system such as git
@@goawqebt6931 I have accepted the notation and yes it reflects the data structure ( as only child nodes know about the address of the parent node) it is not helping understanding of git. I will just say that I totally understand the initial comment and add that git is poorly explained in general.
Yes, it is unintuitive at first. But the arrow direction is important! Git uses it to answer important questions like “what did you change?” and “Is this safe to push?” In general it is much harder to go against the arrows, so you’ll have an easier time if you can check out a later commit and look backwards than vice versa.
Great Video, thank you.
very useful. thanks!
What do you use for resolving merge conflicts? Simply vim?
Yep! The default is vimdiff, which is just vim with a specific config with diff highlighting. I have not found another tool which works better.
Really informative
Thats alooooot, after many years now i feel like i understand git, (at the time of writing the comment im still at 8:47)
I really thought git playground was a thing, but got to know that its simple setup for practice.
Great tutorial, the arrows going backward is a bit confusing though
the most recent commit references the previous commit, not the other way around. it wouldn't make sense for the arrows to point "forward", how can you make a reference to part of the development history that doesn't exist yet?
@@tissuepaper9962 after it is created, easy. If it doesn't exist it can't refer back either
Hi, can you recommend a public repo that can be used to learn advance git? I want to build on a clone.
It's hard to pick a specific one. I recommend first finding an open source project on github that you currently use. Then clone it and make a local branch with some modification that seems useful. If that goes well, fork the repo and add it as a new remote for your clone and push your branch, then open a PR.
@@floatinglittleleavesofcode6473 Thanks! I will do this.
I pretty much exclusively use rebase instead of merging 😅 I just like how simple it keeps my history, merge commits really bug me. I feel like it also forces me to deal with conflicts sooner rather than later.
That's also a valid workflow, but as an exercise try a merge-only workflow some time. I think it forces you to come to grips with git's more advanced features.
Which font you are using in IDE?
Usually just the default monospaced one. But if I choose one specifically, I go for DejaVu.
awesome vid
I've not truely understood the thing with submodules and why it's useful (i never had to use this nor have seen it), but otherwise it's a very useful tutorial
say you want to use two different versions of the same repository in two of your projects. instead of manually downloading each version and keeping them in separate directories like in the olden days, with submodules you can keep those two copies neat and tidy in their respective repositories. if you tried to just clone the submodules into the two project repos, git freaks out about multiple trees.
This seems like a simple complaint when I type it out, but it always gives me trouble understanding git graphs intuitively: The arrows are pointing backwards! I get that it's because it's changes piled on changes, what we're really looking at is a history.
But I hate it
😭
It really helps to flip around the way you think about history in git. Instead of "This is what the code used to be. What happened next?" think about it as "This is the code now. What did it look like before?" Almost every git command approaches history in this way, so it's a good exercise to try it out yourself.
Amazing tutorial
Solid tutorial, definitely a good step to become an expert, but I wouldn't necessarily say that it's all it takes to be an expert in git. For first, it doesn't mention line endings, file permissions, storing binary data, LFS, multiple remotes, fetch --prune, rebase -rerere, !fixup, multiple types of checkout (like --cached), rebase --onto, it doesn't say about octopus merge, multiple roots, push --force-with-lease, files on case-insensitive system (like windows), rebase --exec, cloning via https vs ssh, probably more. Additionally, to be an expert in git, it doesn't just take to know the tools; but more importantly - to be an expert you must *learn* the mindset, what are the ways to achieve some goal. Tools and commands only aid in that.
I agree. This video is mainly about how to start learning the mindset. Once someone has a reasonably accurate picture of how all of these basic commands work, they should be able to figure out the other things you mention on their own.
Saved this for later,
I now undestand why I always say to Git to Fork off... Now that I know that git will Fork off automatically I will be less frustrated...
But I will keep saying to git to Fork off... just for fun!
Can you please tell me an easy way to see history (log), only for a particular file?
Many git commands can take a file after them.
git log PATH_TO_FILE
If you want to see the actual code changes, you can use git log -p PATH_TO_FILE which will output the commits as a list of patches.
git log --follow is good for that. Check git help log
Good theoretical overview. Still some parts were vague. For example the part about rebasing. And about second kind of merging in git (where git is much smarter - 11:11 timecode).
Otherwise thanks
I'm not sure what you found vague about those explanations so it's hard to address your confusion. But I'll try again.
A fast-forward merge happens if and only if your current branch is reachable in the history of the branch your are merging. Then the merge simply updates the branch pointer and no new commits are created.
Rebasing takes a set of commits and creates a brand new set of commits that tries to mimic the original set, possibly applying those commits on a different starting commit. It is hard to be more specific than that because rebasing has a *ton* of options. You can skip some of the original commits, combine some of them, edit some while they are being recreated, etc. There is literally no way to predict what the result of a rebase will be.
`git rebase --interactive` (or just -i) lets you rebase via a little wizard, instead of having to commit and continue constantly. Do read up on it a little, as you need to specify which "commands" should apply to which commits. The free Pro Git book has a chapter on it. URL in the next comment, because I don't know if urls are filtered.
i have a question. If you upload a long image to github, but later you want to delete that image.
Is your repository wasting a lot of memory for storing that deleted image?
i think you called, 'hard reset'
Yes, as long as there are commits in the history with that image present, the repository is going to contain that image, so if the image is 100Mb, then the repository is going to be 100Mb larger. The only way to get rid of it is to alter the git history and remove the image from each commit.
You told us GIT doesn’t care about directories but does is it ignore the permissions on directories also ? That doesn’t seem good
It doesn't ignore the files - if you change permissions on a file, git will correctly mark it as unstaged change, and you can either reset the change or "git add" and "git commit" it. It can be disabled in config, to ignore file permissions, but git stores them by default.
It is an inherent limitation. All directories in a git repo must be rwx or else git can’t operate on them. Permissions like user/group access cannot exist in the repo because there is no guarantee that they will exist on the machine where the repo is cloned.
For example, all of the permissions that github manages regarding repo access, edit permissions, etc are all external to git itself.
@@floatinglittleleavesofcode6473 Thanks for explaining 🙂
Of course has “directories”. That’s literally what tree objects are
Can i get access to slides, please?
Sure thing. silverhammermba.github.io/git-training-slides/
@@floatinglittleleavesofcode6473 thank you so much
Thank you!
⭐⭐⭐⭐⭐
What is the difference with CVS?
The difference is like night and day, far too much to go into in a comment. Suffice to say CVS can at best poorly approximate a fraction of git’s power.
why would ur branch need multiple commits?
It depends on your commit style. Because git is so efficient at internally representing its commits, it encourages you to make many small commits on each branch rather than a small number of large commits. Many people find this style useful because it gives you more control over things like merging, reverting, etc.
Great tutorial but would be better named "git intermediate". An expert is going to know a lot more workflows and will know about things like tags, repo pruning, hooks, and ways to think about where you might find a merge conflict not detected as one.
Perfection
Excellent presentation
nice panda's ears
He said you should use a mergetool with 4 windows.
can someone suggest a mergetool like that?
The default (vimdiff) is the only one I know of that does it out of the box. But technically any editor that can do a four window split can be hooked up to git. The mergetool command can be customized to pass its four arguments to the tool of your choice.
I once set up Sublime to do this for someone.
You totally can rename/move files without delete-create and losing edit history. This is what 'git mv' command made for.