This is a total mess of a repo and you can break everything to learn how to use git.
You should probably install this gist first.
There is no perfect way to use this repo, in fact a lot of it won't be useful but might just be an idea for how to proceed. I'll also include some resources to learn git and it might behoove you to make a similar repo of your own and try to do things like you can find in the commit history:
We're going to learn how to merge first, then rebase. Open up two terminals side-by-side and run these commands (ask ChatGPT what they do if needed). I'm assuming we're starting with only one branch, master. Terminology
- repository in Git is a directory where Git has been initialized to start version tracking. It is essentially your project's folder which contains all of the project files and the metadata about the project history stored in a subdirectory named .git. Each Git repository is standalone and contains the full history of the project. This means that if you have a copy of a Git repository, you have the entire project history on your local machine, making it robust against network failures or server crashes.
- Local repository: This is the repository on your local machine where you make changes to your files. It's created using the git init command or cloned from a remote repository using git clone.
- Remote repository: This is the repository that is hosted on a server on the internet or in a network. This is where developers push their changes for collaboration with others. Popular hosting services for remote Git repositories include GitHub, GitLab, and Bitbucket.
- cloning creates a local copy of a repository, while forking creates a server-side copy of the repository under your own account.
- upstream : is used to refer to the main branch or the original repository from which you cloned or forked. More specifically, it often refers to the branch that you're tracking in the original repository.
- Fork: Forking, on the other hand, is a feature provided by hosting services like GitHub and GitLab that lets you create a personal copy of someone else's repository under your own account. This is used when you want to contribute to a project that you don't have write access to. After forking, you can clone the forked repository to your local machine, make changes, and then__propose those changes back to the original repository by creating a pull request.__The original repository owners can then review your changes and decide whether to merge them into the original repository.
- Clone: Cloning is a Git operation that__creates a copy of a repository on your local machine.__When you clone a repository, you get a new directory on your computer with all the files and history of the repository. You can make changes to these files, commit those changes to your local repository, and push your commits back to the remote repository__if you have write access.__Cloning is typically used when you're starting to work on a project that you have write access to, or when you want to work on a project locally and don't need to publish your changes. -a branch is essentially a unique set of code changes with a unique name. Each repository can have one or more branches, allowing you to work on multiple features simultaneously without them interfering with each other. The main branch (the one where all changes eventually get merged into) is usually called master or main.
- Each branch in Git is a pointer to a specific commit, marking the 'tip' of a series of commits (i.e., the history of work done). New commits are added to the end of this series and pointed to by the branch.
- When you create a new branch, Git creates a new pointer for you to move around. This means you can switch branches quickly and easily, as Git simply needs to move the__HEAD pointer__to a different commit. Creating a new branch doesn't change the repository; it simply adds a new pointer to the existing commits.
- HEAD which is the latest commit of the currently checked out branch. When running
git log
, HEAD will be pointing to a branch.- Detached HEAD: This is the state when HEAD points to a specific commit, instead of a branch. This can be risky because any changes made in this state won't belong to any branch and will be lost when you check out another branch. This situation usually occurs when checking out commits or tags instead of branches.
- You can reattach HEAD to a branch by checking a branch out again with
git checkout branchname
- You can reattach HEAD to a branch by checking a branch out again with
- Attached HEAD: This is the usual state of HEAD when it points to the tip (the most recent commit) of the current branch.
- Detached HEAD: This is the state when HEAD points to a specific commit, instead of a branch. This can be risky because any changes made in this state won't belong to any branch and will be lost when you check out another branch. This situation usually occurs when checking out commits or tags instead of branches.
- HEAD which is the latest commit of the currently checked out branch. When running
- commit is a snapshot of your work that you've saved to your repository.__Every commit is uniquely identified by a SHA-1 hash__which represents a specific set of changes. Common SHAs mean a common history between branches. When SHAs change due to a command, that means the history has changed.
- history : refers to the sequence of commits made in a repository over time. Each commit represents a set of changes made to the project and is linked to the commit that came before it. This linking forms a chain of commits, which we call the commit history or simply history.
- diverge : when Git tells you that histories have diverged, it means that there are commits in your current branch that aren't in the branch you're trying to merge with, or pull from, and vice versa. Divergent histories can be rectified with either a rebase or a merge.
- git-rebase - Reapply commits on top of another base tip
- Base: The base, in the context of a rebase, is the commit on which you want to base your work on. When you run a rebase operation, Git finds the common ancestor of the current branch and the one you're rebasing onto, then applies each of the changes from your branch onto the head of the branch you're rebasing onto. This base commit is effectively the starting point of your changes.
- Tip: The tip refers to the most recent commit of the branch, also known as the__ HEAD __. In the context of a rebase, it means the latest commit in the branch that you're trying to rebase.
- git-merge - Join two or more development histories together
-after a merge, do you have to add a commit?
- In Git, when you perform a merge, a new commit is automatically created if the merge involves divergent paths from the base commit (the common ancestor commit). This new commit is called a merge commit, and it has multiple parent commits.
- When you run git merge branchname, Git attempts to automatically merge the changes from the specified branch into the current branch. If the changes don't conflict, a new commit (the merge commit) is automatically created, and you don't need to do anything else.
- If there are conflicts between the branches that Git can't resolve on its own, Git will pause the merge and tell you which files have conflicts. You'll have to manually edit those files to resolve the conflicts, and then stage the resolved files with git add. After resolving all conflicts and staging the changes, you run git commit to complete the merge with a new commit. This commit is also a merge commit, but its creation was not automatic, because manual conflict resolution was involved.
- So, after a merge, you don't usually have to add a new commit yourself, because Git does it for you. The exception is when there are merge conflicts that need to be resolved manually. After resolving these conflicts, you will have to complete the merge commit yourself. -what does it mean to 'fast-forward' in git?
- Fast-forward is a term used in Git when you are merging one branch that is ahead of the current branch, and there are no new changes on the current branch since it diverged.
- Let's consider an example. You're working on a project and you have a master branch. You decide to create a new branch called feature from master and make some commits. During this time, no other changes were made to the master branch. If you checkout to master and merge feature, Git doesn't have to create a new merge commit. Instead, it can simply fast-forward the master branch pointer to point to the same commit feature is pointing to, as this will bring master up to date with feature.
- This operation is called a fast-forward because you're moving the branch pointer forward along the commit history, making it point to the most recent commit.
- git-rebase - Reapply commits on top of another base tip
- conflict : Conflicts in Git occur when two or more changes clash with each other, and Git is unable to determine which change should take precedence. This typically happens when multiple developers are working on the same project simultaneously, and their changes overlap.
- The
<<<<<<< HEAD
line marks the beginning of the conflicting section. - The
=======
line separates the conflicting changes. - The
>>>>>>>
branch-being-merged line marks the end of the conflicting section.
- The
- To resolve the conflict, you need to manually edit the file to choose one change or the other and delete the added symbols. After you've resolved the conflict, you use git add filename to mark the file as resolved, and then git commit to finish the merge.
- diverge : when Git tells you that histories have diverged, it means that there are commits in your current branch that aren't in the branch you're trying to merge with, or pull from, and vice versa. Divergent histories can be rectified with either a rebase or a merge.
- history : refers to the sequence of commits made in a repository over time. Each commit represents a set of changes made to the project and is linked to the commit that came before it. This linking forms a chain of commits, which we call the commit history or simply history.
Here's an example of what a conflict might look like:
<<<<<<< HEAD
This is some text from the current branch.
=======
This is some different text from the branch being merged.
>>>>>>> branch-being-merged
The left terminal is meant to only show graphs of history to become accustomed to how it looks.
git log --pretty=oneline --graph --decorate --max-count=5 --all --abbrev-commit
-> only run this in the left terminal, one per time between all the right terminal commands- The latest commits across all branches will appear first, so if HEAD is not on the top line, there are commits that have occured more recently than the currently chec ked out branch.
- You can simply press up to rerun the command, instead of having to type it out every time.
The right terminal is for running the majority of commands. Mark off the commands as you run them by placing an X inside the - [ ]
so it looks like - [X]
. This will trigger 'changes' that you can commit so you can view the history. The only required commands are in the top level of this bullet list (marked with -[ ]). Nested commands are just explainations. Also, Read the docs. Nested commands depend on the context of parent commands in this tree. If you try running the nested commands, you might get stuck in a state and have to run git checkout master
then git fetch origin
and git reset --hard origin/master
to start over.
-
git status
-> good to run anytime, shows status.git commit -am "commit message"
is a shortcut combination to stage and commit your changes. (git add .
andgit commit -m "commit message"
)git add --all
is a way to stage your changes.git commit --amend
is a way to change the commit message of the previous commit, but this will create a new SHA (changes history)
- To throw away 'Changes not staged for commit' run
git checkout .
- To throw away changes that have been staged (i.e. after using
git add .
shortcut orgit add --all
for all files) rungit reset
(optional--soft
--hard
)
-
git checkout master
- delete all other branches with
git branch -d other-branch-name
passing-f
if needed.
- delete all other branches with
-
git commit -m "tutorial start"
-
git checkout -b merge-branch
- make sure tocheck the checkboxs up to this point and save the file
-
git commit -am "changes made in merge-branch"
- remember to run the left terminal between every command so you can see what the repository is doing. Right now *** HEAD *** should be pointed at
merge-branch
. Also, the latest commit "changes made in merge-branch" should be on top, while the master branch is pointed to the second commit "tutorial start". This meansmerge-branch
is one commit ahead of master. This can be verified by runninggit rev-list --count master..HEAD
. To see how many commitsmaster
is ahead ofmerge-branch
try runninggit rev-list --count HEAD..master
. The result should be 1 and 0 respectively.
- remember to run the left terminal between every command so you can see what the repository is doing. Right now *** HEAD *** should be pointed at
-
git checkout master
- remember to check the checkboxes as you go (remember to save the file too), and keep producing new graphs to see what changes.
-
git commit -am "changes on master"
- This should make it so master is back in the first position and HEAD should be pointed at master, because it's the currently checked out branch. Both merge-branch and master can't fit on the same line, so merge-branch is indented. However if you run
git rev-list --count merge-branch..master
again, it should show 1, which means master has one commit that merge-branch does not. Comparitively, if you reverse the command likegit rev-list --count master..merge-branch
, it should show that merge-branch also has one commit that master does not. This means that "histories have diverged" and we need to rectify it with either a rebase or a merge. - Since we're working on the merge-branch, we'll perform a merge.
- This should make it so master is back in the first position and HEAD should be pointed at master, because it's the currently checked out branch. Both merge-branch and master can't fit on the same line, so merge-branch is indented. However if you run
-
git merge merge-branch
from master- This command will replay changes made on merge-branch since it diverged from master(one commit ago) until the current commit of merge-branch. It does this on top of master.
- This should result in a conflict, where the changes for HEAD (master) are listed on top, even though we added them second. This means we need to move them down, below the merge branch and remove the symbols.
- Notice the conflicts appear in a special file, where the title is marked with an exlamation point and the terminal mentions 'Merging'. Since we're currently resolving conflicts, we'll need to close this file and add this merge of both files as a new commit.
- What happens if you start on merge-branch and run
git merge master
?- When you issue the command git merge, you are saying *** "merge the specified branch into the current branch." *** In other words, you are bringing changes from the specified branch into the one currently checked out.
-
git commit -am "merged merge-branch into master"
- When you run the graph, you should see HEAD pointed at master as the most recent commit and merge-branch one commit behind. We'll fix this, but first...
git rev-list --count master..merge-branch
will give you the number of commits that are in the__merge-branch__but not in master.- Should be 0, because we merged the changes of merge-branch into master.
git rev-list --count merge-branch..master
will give you the number of commits that are in the master but not in merge-branch.- Should be 2, because there was the original commit we made on master("changes on master") and the subsequent merge commit ("merged merge-branch into master")
git log --pretty=oneline --max-count=5 --abbrev-commit
to see the history of the currently checked out branch. In this case master which shows the "changes made in merge-branch" commit and the associated merge-branch just below.- The line that reconnects merge-branch to master, indicating the changes were included back into master.
- The stars indicate which branch the commit lives on.
- *** Comparing this to the same graph in the below rebase is key to understanding the difference between a merge and rebase ***
- Now if you wanted merge-branch to be at the same commit, you would want to merge changes from master into merge-branch, which would require checking out merge-branch and running
git merge master
. Then look at the graph and you'll see both branches on the same commit.
-
git checkout master
-
git commit -am "rebase start"
- Remember to check the box to trigger a change to the file.
-
git checkout -b rebase-branch
- One of the things to notice is that if you make changes while on one branch, you can move to a new branch without needing to check them in. This is not the case if you're moving to an existing branch. You'll have to manage your changes by either
- Commiting them
- Throwing them out with something like
git checkout .
orgit reset
- Stashing them with
git stash save "Your descriptive message here"
andgit stash pop
- One of the things to notice is that if you make changes while on one branch, you can move to a new branch without needing to check them in. This is not the case if you're moving to an existing branch. You'll have to manage your changes by either
-
git commit -am "changes made on rebase branch"
- Check the graph, HEAD should be pointing at rebase-branch and "changes made on rebase branch should be on top"
-
git checkout master
-
git commit -am "changes on master before rebase"
- need to add some changes to master branch that is not on rebase-branch.
-
git checkout rebase-branch
- changes are alreadyhere from before and we just need to run a rebase.
-
git commit -am "more changes on rebase-branch before rebase"
- Mark this complete before rebase to avoid common changes bug.
-
git rebase master
- When you run the command
git rebase
, you are saying "reapply the commits from the current branch onto the branch I specify." In this case, we're applying the changes "changes made on rebase branch" onto __ master__, since we specified master. - The special file with the conflicts may appear. Once you resolve the conflicts, close this file and proceed to the next command.
- When you run the command
-
git add .
-
git rebase --continue
- Now you'll see "changes made on rebase branch" on top, with master one commit behind. HEAD is pointed at rebase-branch of the latest commit.
- *** Compare this graph to the graph mentioned above to understand the difference between rebase and merge ***
- To bring master to the current state of rebase-branch (which now has "changes on master before rebase") continue with the following commands
-
git checkout master
-
git rebase rebase-branch
- Remember rebase means "reapply the commits from the current branch onto the branch I specify." Since there are no new commits to move into rebase-branch, it has nothing to 'pick up and rebase' kinda like a fat snowman picking up his body and putting on another branch, so it'll just move the pointer from the commit "changes on master before rebase" to "changes made on rebase branch".
- As a reminder, merge means "merge the specified branch into the current branch."
- Some final notes, the way git determines if the how many commits ahead/behind a branch is from another is by comparing the SHAs, so if the SHAs are different, it'll think the history is different, even if the changes are the same. This means you'll have to account for this if you ammend commits, which changes the SHAs.
Both merge
and rebase
in Git are designed to integrate changes from one branch into another, but they do it in different ways and the resulting history will look different.
Merge: git merge takes the contents of a source branch and integrates it with the target branch. When you merge a branch, it takes the contents of that branch and merges it with your current branch all at once. This creates a new "merge commit" in the history that has two parents, maintaining the historical context of the branch.
Rebase: git rebase moves or combines a sequence of commits to a new base commit. Essentially, it's like saying "I want to base my changes on what everybody else has already done." When you rebase, Git takes the changes made in each commit of your current branch that are not in the upstream branch and saves them away temporarily. It then applies the changes of the upstream branch to your base commit, and re-applies your changes on top of that, one by one. This results in a linear history.
History: merge preserves the history of your commits exactly as they were, while rebase rewrites the commit history in order to produce a more linear project history.
Merge Conflicts: In case of overlapping changes, with merge, you'll only need to resolve conflicts once. With rebase, conflicts need to be resolved for each commit separately, as changes are applied one by one.
Traceability: Merge keeps all the past history of commits, making it easy to see when and how changes were made. Rebase provides a more simplified and linear project history, without the noise of non-significant commits.
Safety and Collaboration: merge is a safe operation that won't alter existing history, making it a good choice for public or collaborative branches. rebase alters commit history, which can be problematic for anyone else who's already using the existing branches, so it's generally recommended for cleaning up local branches.
In a nutshell, if you want to combine your changes with another branch while preserving chronological commit history, use merge. If you want to make your feature branch up to date with the latest code from the target branch, while maintaining a clean, linear history, use rebase.
- Git School specifically, start here
- Intro to Git in 16 mins
- 9 Beginner Friendly Tutorials
- Writing better commit messages
- Github Docs
- git scm
- Github Community
- Git Explorer
- Basic writing and formatting syntax
- The Markdown Guide
- Create Awesome Git readMe Profile
- 10 Standout GitHub Profile READMEs
- Publishing sources for GitHub Pages sites
- Awesome Developer Profile
- GitPals
- Take advantage of Git Rebase
- How to rebase in Git: Explained Step by Step
- Effective Git for Solo Developer
- Github 1s
- Git ignore templates
- Gitlab
- About Gitlab
- https://channel9.msdn.com/Shows/Azure-Friday/Learn-how-to-deploy-NET-Core-apps-to-Azure-with-GitHub-Actions
- GitHub Actions
- DevSec Ops
- Gist
- Pluralsight Course on DevOps with Github and Azure: Implementing CI/CD with GitHub Actions
- Git Guardian
- Code Scanning
- Github Cli 1.0
- GitKraken Client
- Lazy Git Client
- Merging preserves history exactly as it happened (same SHAs) and merges it into one single timeline
- rebasing creates new commit (SHAs for changes in history)
git log --all --decorate --oneline --graph --max-count=5
shows all branchs graph history, limiting to last 5 commits;
- The process was create two subsequent commits on branch f1, then 2 commits on branch f2, then a third commit on f1 and run either a merge or rebase.
In the below image, on top you can see the history after a rebase. On the bottom the history after a merge:
- The original f2 branch for the rebase is not shown, but it wouldn't have the same SHAs as what is shown.
- move to branch you want to move changes into then run
git merge branchWhereChangesAreComingFrom
- another difference is which branch you start the operation on. During a rebase you start on the branch you would like to detatch from the commit that is the common origin (origin commit that other branch you want to move the commits onto has in common) and write the branch you're moving the commmits onto. For instance if I'm trying to move f2 commits onto f1, I would start on f2 and write
git rebase f1
- when you rebase with
git rebase master
when on a featureBranch based on older commit, HEAD is pointed at the master branch, while the changes you're rebasing onto master are the incoming changes. Further, the incoming changes, in the resolve conflicts view, come second, even if the work precedes what is already in master. - Also, have to run
git add .
thengit rebase --continue
after resolving conflicts - When conflicts have been resolved, you'll want to run
git push origin <featureBranch> --force-with-lease
without pulling. article on --force-with-lease - If it drops into an interactive rebase, update the commit message and maybe add another commit with additional changes
git tag -a v1.2 -m "learning how to tag things"
- make tag with messagegit tag -n
show tags and messagegit push --tags
push tags
git tag <tag_name> HEAD
(for the last commit)git tag <tag_name> HEAD~1
(for the commit before HEAD)git tag -a <tag_name> HEAD -m "message"
git reset --soft HEAD~3
combines this many commits -> includes 3 commits total starting with HEAD
good article on deleting commits
git reset --hard HEAD~N
where N is the number of commits to remove
git reflog
will show you even deleted commits that can be checked out with agit reset --hard <sha>
git commit --amend