- Version Control
|ID||Status||Learning Item||Type||Related Project||Date Completed|
|1||Learn Git Branching (34/34)||Tutorial||8/24/2020|
|3||Not Started||Using Version Control in VS Code||Official Docs|
|4||Not Started||How to Scrub Sensitive Information from Git||Blog|
- Quick Reference
- Reversing Changes
- Moving Work Around
git branch newBranch git checkout newBranch
Or, in a single command:
git checkout -b newBranch
git merge newBranch will merge
newBranch into our current checked out branch.
Rebasing takes a set of commits, “copies” them, and places them somewhere else. This can be used to make a linear sequence of commits. The commit log / history of the repository will be a lot cleaner if only rebasing is allowed.
While bugFix is checked out, if we use
git rebase master:
git rebase c2 c3 can also be used to copy commits below a certain target if specified as a second argument:
HEAD and Relative References
HEAD is the symbolic name for the currently checked out commit.
By checking out the hash for a commit instead of the label, we can detach the
HEAD from the label.
Referencing commits by their hash isn’t always practical, so we can take advantage of relative commits
^ - Move upwards one commit at a time
^2 - Move upwards one commit at a time, but specifying alternate parents from the immediate parent
~3 - Move upwards a specified number of times (3, in our example)
git checkout master^ is equivalent to “checkout the first parent of master”:
These navigation commands can be chained together to quickly navigate across the tree:
git checkout HEAD~^2~2
The force command
-f can be used for force branches to different locations.
git branch -f master HEAD~3:
git reset will move a branch backwards as if the commit had never been made in the first place. This affects the local repository.
git revert, on the other hand, will reverse a commit but push them remotely. It does so with a new commit forward in-line that undoes all of the referenced commits changes.
Moving Work Around
git cherry-pick <Commit1> <Commit2> <...>
`cherry-pick’ is a very straightforward way of saying that you would like to copy a series of commits below your current location (HEAD).
git cherry-pick c2 c4:
Cherry-pick will take a commit from anywhere in the tree as long as that commit isn’t an ancestor of HEAD.
All interactive rebase means is using the
rebase command with the
If you include this option, git will open up a UI to show you which commits are about to be copied below the target of the rebase. It also shows their commit hashes and messages, which is great for getting a bearing on what’s what. You can do things like reorder, omit, squash, and more!
Locally Stacked Commits
rebase can be a great way to push the final results of a laborious bug tracking and fixing hunt, without including all of the debugging along the way.
Let’s say you have some changes (newImage) and another set of changes (caption) that are related, so they are stacked on top of each other in your repository (aka one after another).
The tricky thing is that sometimes you need to make a small modification to an earlier commit. In this case, design wants us to change the dimensions of newImage slightly, even though that commit is way back in our history.
We can overcome this difficulty by doing the following:
- We will re-order the commits so the one we want to change is on top with
git rebase -i
- We will
git commit --amendto make the slight modification
- Then we will re-order the commits back to how they were previously with
git rebase -i
- Finally, we will move master to this updated part of the tree
This worked out fine, but has the potential to introduce rebase conflicts. Using cherry-pick can be a more concise form of manipulating location.
Tags are a way of marking commits as something more permanent than a branch. They do not move as commits are added, and you cannot check out a tag and then complete work in the tag, and checking out a tag will create a detached HEAD. It serves as an anchor in the commit tree to designate a particular spot, such as a milestone or version release.
git tag v1 C1:
In our example, we added the tag to C1 explicitly, but the tag will be added to HEAD if a destination is omitted.
Because tags serve as such great “anchors” in the codebase,
git describe can be used to describe where you are relative to the closest “anchor” (aka tag). Git describe can help you get your bearings after you’ve moved many commits backwards or forwards in history.
Git describe takes the form of:
git describe <ref>
<ref> is anything git can resolve into a commit. If you don’t specify a ref, git uses your current location (HEAD).
The output of the command looks like:
<tag> is the closest ancestor tag in history,
<numCommits> is how many commits away that tag is, and
<hash> is the hash of the commit being described.
git describe master=>
v1_2_gC2, 2 commits from v1, hash C2
git describe side=>
v2_1_gC4, 1 commits from v2, hash C4
git describe c1=>
v1_1_gC1, 1 commits from v1, hash C1
git describe v2=>
v2, v2 is v2 -
describeis relative to anchors!
Remote branches have a required naming convention of
<remote name>/<branch name>, for example,
git clone is used to create a local copy of a remote repository.
git fetch will fetch data from a remote repository. It does this with two main steps:
- Download the commits present in the remote but missing from local
- Update where our remote branches point (i.e
git fetch will NOT change anything about your local state.
git pull is essentially a macro for
merge-ing remote changes in a single command.
These two sets of commands accomplish the exact same result:
git fetch; git merge origin/master
git fetchwith local master checked out
git push, on the other hand, will upload local changes to the remote repository.
This works great, until the Git history diverges. In this case, it is ambiguous how the files should be incorporated. This can be resolved a few different ways.
- By fetching and rebasing:
git fetch; git rebase origin/master; git push
- Or, by using a pull with a rebase command:
git pull --rebase; git push
- By fetching and merging:
git fetch; git merge origin/master; git push
- Or, by using a normal pull:
git pull; git push
Rebase vs. Merge
** Rebasing **
- Makes your commit tree look very clean since everything is in a straight line
- Modifies the apparent history of the commit tree
** Merging **
- Preserves all tree history
- Can be very busy
It all comes down to individual and team preference!
Tracking Remote Branches
When a repository is cloned, native connections are created between identical local and remote branches. However, you can also manually create links between local and remote branches!
git checkout -b foo origin/master will create a new local branch
foo that tracks to the remote
Similarly, you can use
git branch -u origin/master foo to have the local branch
foo track the remote
foo is already checked out, you can omit it from the original command.
Advanced Push, Fetch, and Pull
git push <remote> <place>
git push origin master
Go to the branch named “master” in my repository, grab all the commits, and then go to the branch “master” on the remote named “origin”. Place whatever commits are missing on that branch and then tell me when you’re done.
You can also optionally change the destination:
git push origin <source(local)>:<destination(remote)>
This is called a “colon refspec,” which is a word for a location that git can resolve, like
git push origin foo^:master
This command takes 1 commit above
foo (C2) and pushes it to remote and attaching it to the remote
master, like this:
You can also specify a new remote branch name as the destination value and the new remote branch will be created.
<source> is not tightly controlled, and can be used in some specific ways.
git push origin :foo, for example, will delete the remote
fetch works very similar to push, just in reverse! Unlike
push, however, the changes are fetched to your local copy of the remote branch by default and NOT your local branch. This is to help prevent losing work. That being said, you can use the
<source(remote)>:<destination(local)> syntax to force a fetch to a local branch if desired.
You also can’t fetch commits onto a branch that is checked out.
fetch will create branches if a specified destination does not already exist.
Also similar to push, an empty
<source> can be used to create a new local branch.
git fetch origin :bar will create the local
bar branch. Seems more complicated than it needs to be, but hey.
As a reminder,
git pull is really just shorthand for
git fetch; git merge, even with all of the extra syntax.
git pull origin bar~1:bugFix is equivalent to
git fetch origin bar~1:bugFix; git merge bugFix