git
git (plural gits) (British, Ireland, Australia, New Zealand, slang, derogatory) A silly, incompetent, stupid, or annoying person (usually a man).
“the stupid content tracker”
A distributed version control system.
Version control system — a system for tracking different versions of a set of files.
Distributed — there’s no central server; every developer has a complete history of the code on their computer.
A service that hosts git repositories and provides functionality on top of git for managing collaborations.
Github model is extremely good for open-source software where anyone might want to contribute to any project yet project owners need a way to manage potential contributions.
Repositories
Commits
Branches
Merging
A collection of files with their complete history.
Every repository is a multiverse of different timelines that split and merge.
A unique point in the history of the multiverse.
Most commits represent a change to one or more files in the repository.
Commits also have a relationship to all the commits that came before them in whatever timeline (or timelines) that they are part of.
A commit identifies the contents of the repository at that point
A commit represents the set of all commits that they depend on.
Note that while a commit determines the state of the repository contents there can be different commits that put the repository into the same state, i.e. that got to the same place via a different series of commits.
A particular named timeline in the multiverse.
At any given moment a branch points to a particular commit but when you make a new commit, the current branch is updated to point to that new commit.
The process of creating a branch.
Usually from whatever commit the current branch is at but can be from any existing commit.
When you create a branch in the normal way, at the point the branch is created the new branch and the old branch point to the same commit.
After that, new commits are added to the new branch and the old branch is unchanged.
The process of combining two timelines into one.
If you have two commits (probably named by branches) representing divergent timelines in your repo, you can make a new commit that depends on both of those commits, unifying the timelines.
Each circle represents a commit.
Lines represent branches.
Merging two commits also requires that we merge the contents of the files in the repository as they exist in each of those commits to produce a new set of files that represents how we want to combine all the changes.
Git will help with combining the contents of the files from the commits you are merging.
Conflicts arise when unrelated commits make changes to the same part of the code.
Git will recognize this and force you to resolve them.
Let’s start with this file in main
:
public class Foo {
private int x = 42;
}
git switch -c branch-1
Change the initial value of x
to 43
:
public class Foo {
private int x = 43;
}
git commit -am "42 -> 43"
main
git switch main
Add a new line right below the old line:
public class Foo {
private int x = 42;
private int y = 100;
}
git commit -am "Add y"
Now branch-1
and main
have unrelated commits to Foo.java
Let’s merge!
$ git merge branch-1
Auto-merging Foo.java
CONFLICT (content): Merge conflict in Foo.java
Automatic merge failed; fix conflicts and then commit the result.
Foo.java
now:public class Foo {
<<<<<<< HEAD
private int x = 42;
private int y = 100;
=======
private int x = 43;
>>>>>>> branch-1
}
Markers show us the conflicting sections.
HEAD
is a special name for “the commit we are currently on”. A.k.a. main
in the current scenario.
public class Foo {
private int x = 43;
private int y = 100;
}
We take the new value of x
from branch-1
and the new variable y
from main
.
Note, it’s up to us to determine the right resolution. Maybe that change from 42 to 43 doesn’t actually make sense in the context of how we’re merging the two branches. Only a human can know.
So if there’s no conflicts, everything is fine, right?
Well, not necessarily.
Suppose in one branch you rename a method from foo
to bar
and change all the places that call it.
In another branch, made before the name change, you add a new call to foo
.
There’s no textual conflict but if you merge those two branches but the code will not compile.
You do need to exercise some care when merging to make sure the result of the merge is actually what you want. I.e. it does compile cleanly, passes whatever tests you have, etc.
On the other hand, it’s just another change—you can always see exactly what happened in the merge and fix things up if need be by making a new commit.
You can make a branch from any commit. And you can merge any commit into a branch.
This lets you go back in time (make a branch from some old commit and pretend you’re living in the past.)
It also lets you combine development branches before you merge them to main.
Don’t go nuts with this though.
git add |
add a file to be committed |
git mv |
rename a file so git knows about it |
git commit |
commit the currently added files |
git pull |
fetch and merge commits for the current branch |
git merge |
merge a branch |
git help
man git-commit
, etc.