1 | knitr::opts_chunk$set(eval = F) # disable eval |
Basic Commandline navigation
Key terminal commands
1 | help # some basic information |
some advanced stuff
general
1 | ls -ld -- */ # list all directories in the working directory |
regex
1 |
|
links
You can create an alias (aka known as symbolic link) to an file or directory.
1 | ln -s vim_test vim_symlink # create vim_symlink that points to vim_test |
Interactive usage
1 | # e.g. after |
Auto completion
[TAB]
1 | [TAB] #will autocomplete what you write if there is an object that matches up. |
[Stars] \ & \*\ **
1 | # you might want to use auto completion with the star (*). It can be leading or trailing: |
Two consecutive asterisks (“**”) in patterns matched against full pathname may have special meaning:
- A leading
**
followed by a slash means match in all directories. For example,**/foo
matches file or directory “foo” anywhere, the same as pattern “foo”.**/foo/bar
matches file or directory “bar” anywhere that is directly under directory “foo”. - A trailing
**
matches everything inside. For example,abc/**
matches all files inside directory “abc”, relative to the location of the .gitignore file, with infinite depth. - A slash followed by two consecutive asterisks then a slash matches zero or more directories. For example,
a/**/b
matchesa/b
,a/x/b
,a/x/y/b
and so on. - Other consecutive asterisks are considered invalid.
Files to be ignored by git
If the file was not tracked so far
Basic
Explicitly list files/directories to be excluded from version control. This is the simplest and most used case.
To specify which files you don’t want to be tracked by git, create a file called .gitignore
(no extension!) in the relevant directory in which all the file characteristics that identify files that should not be tracked are stored. You can create and open the file like this
1 | touch .gitignore # creates an empty file .gitignore |
Now, within TextEdit, use one line per specification, for example:
1 | text1.txt # uniquely identifies the file text1 not to be tracked by git |
Save the file and close the window. All files starting with a dot are system files and can’t bee seen by the user in the Finder. You may see them if you type ls -la
on the command line. If you just want to see the content of the file printed on the console, type cat .gitignore
.
Advanced
Instead of providing a list with files to ignore, you might want to include a list of files to include. Then there is two cases.
The file is the git directory
This is the easier case. Simply add the files not to be tracked precending an exclamation mark. E.g if you don’t want to ignore everything except for .Rmd files in the directory. write the following in your .ignore
file:
1 | ./* # ignore everything |
The file is the a git subdirectory
In this case, you have to manually include every directory that is a parent directory of what you want to include. For example, if you want to unignore (that is, add to git version control) all .R
and .Rmd
files in the subdirectory r-project
in the folder code
but nothing else, you have to go like this
1 | r-project/* # exclude the folder r-project |
Note from Stackoverflow: The trailing /* is significant:
- The pattern dir/ excludes a directory named dir and (implicitly) everything under it.
With dir/, Git will never look at anything under dir, and thus will never apply any of the “un-exclude” patterns to anything under dir. - The pattern dir/* says nothing about dir itself; it just excludes everything under dir. With dir/*, Git will process the direct contents of dir, giving other patterns a chance to “un-exclude” some bit of the content (!dir/sub/).
If the file was previously tracked
This basically involves removing a file from the version control but keep the checked out version in the local repository.
1 | git rm text.txt --cached # or rm -- cached text.txt |
Now the file is added to the list “Untracked files”, but still in the local repo. Now, you can add the file to your .gitignore
file.
Staging area
Basics
1 | git init # initialize a repository |
Advanced
1 | git reset HEAD text1.txt # remove textfile text1 from the stagging area if it |
Renaming files
If you want to rename a file, you have multiple options:
Rename the file in the finder
Although straight forward, this method has a fundamental disadvantage: git won’t recognize this file as the pre-reamed version of the file easyily, so it thinks of it as a new file, whereas the old one has been deleted. Hence, you loose all the history if you decide to go for this option.
Rename the file from the commandline
An alternative is to use git mv oldname newname
to avoid the problem described above. git will recognize the file as renamed, as you can see with git status
.
1 | git mv 01-preprocessing.do 01a-preprocessing_mainform.do # rename without move or |
After having committed the renaming, the changes are tracked, but by default, not shown, since the file has a different name now. To show the changes, we need to add the option -- follow 01-preprocessing.do
. Note that this is the old name in the old directory.
1 | git log ./01-code/01-prepcoressing.do # won't list any commits since nothing committed after renaming |
However, keep in mind that renaming should be avoiding when other people use your code because all references to it will be invalid, which means it can break even old versions.
Committing
Basics
1 | git commit -m "hi this is a commit" # commiting staged commits to current branch |
Advanced
1 | # skip staging |
Stashing
Sometimes, you will find yourself working on something that is not yet ready to
commit, but then suddenly you need to work on something else (e.g. an urgent
bug-fix). In this case you want to save your current changes to a clipboard
without committing them yet. In this case, git stash
is your friend. It
puts your changes aside and leaves you with a clean working directory.
1 | git stash # put changes aside |
You can also get a list of your stashes, the top one being the most recently
added.
1 | git stash list |
To get a stash back, you have multiple options
1 | git stash pop # restores latest stash and removes (!) it from your clipboard |
You can use git stash save 'my title for this stash'
to create a title for a
stash you saved so you can recognize it easier later.
Also, if you don’t want to stash all changes, do git stash -p
to select all
changes you want to stash with y
and abandon changes you don’t want to stash
with n
.
If you wish to stash some of the changes, but not all, you can add the files
you want to stash to the stagging area with git add file1 file1
.
Cloning
You might want to join an existing project and get a local copy from a remote repository in an URL. Some repos might be protected with password. Not the one below.
1 | git clone https://github.com/gittower/git-crash-course.git # clone example repo |
Working with branches
show, create, delete and rename barnches
1 | git branch # show all branches (green star shows current branch) |
Switching branches
1 | git checkout # dischard changes in stagging area. |
Integrating a full branch via merging
As soon as you are done with your work, merge the changes back to master
.
Imagine the following scenario:
- You have a clean branch
master
- You branch out to fix an issue with creating a new branch
hotfix_212
withgit checkout -b hotfix_212
- You are done with the fix and want to merge back.
- In the meanwhile, you changed some code lines in
master
as well. - You committed all your changes in both
hotfix_212
andmaster
Case 1: No conflicts
The easy case, where you don’t have conflicts between the branches (i.e. you did
not change the same line of code in both branches, which you can check with git diff master..hotfix_212
) has the following workflow.
1 | git checkout master # switch to the master branch |
Case 2: Conflicting changes
Manual resolve
It gets a bit more tricky if you have deleted one file in one of the branches
or if you changed the same line of code in both branches. However, start the same
way.
1 | git checkout master # switch to the master branch |
Now, git will tell you that there is a conflict, so the merge is put on hold.
Open the file of conflict (e.g. conffile.txt
) and you will see that git
separated the conflicting sections with >>>>>>>>>>>>>>>>>>> and >>>>>>>>>>.
Resolve the conflicts by rewriting those lines and finally get rid of the markers.
Alternatively, you can configure a merge tool like diffuse or diffmerge.
Add the file to the stagging area and commit the changes as usual. This will
complete the merge.
1 | git add conffile.txt |
Using merge strategies
Instead of resolving by hand, you can apply merge strategies and corresponding
options. Strategies are specified with -s
, options follow after -X
.
The default strategy for a merge is recursive
. So git merge -s recursive
is
a verbose form of git merge
. If we want to merge a branch into another and
we have conflicts (because some lines were changed in both branches) we can
specify options how to proceed. Two popular options are ours
and theirs
.
Let’s look at an example. We are done with a new feature on our branch
devel and we want to merge it into master. In the development process, a hotfix
was committed to master, for which we accounted in devel already. Therefore, we
anticipate that there will be conficts in the merge and that we want to use
the devel version whenever there are conflicts, since we already
accounted for the problems. We would proceed as follows:
1 | git checkout master |
We could also imagine a scenario where we want to merge changes from devel
into master, but conflicts should be solved in favor of master
1 | git checkout master |
Note that above area all options to the strategy recursive.
Something you are less likely to want is to merge a branch
into another but actually discarding all changes introduced by this other
branch. This would be the ours merge strategy. Suppose there is a
devel branch and it lead to nothing, you just want to “close” the branch and
merge it into master, without actually taking any commits from devel. You would
do
1 | git checkout master |
Another use case is if you don’t want to integrate all commits from devel
into master. Let’s say you want to merge everything up to head~3
, nothead~3
but all commits after head~3
again. You could do the following:
1 | git checkout master |
Aborting a merge
If you want to undo a merge whilst you are in the middle of it, just use
1 | git merge --abort |
If you already finished the merge but wish to undo it, simply restore the commit
on the master branch before the merge, e.g. However, you need to have the branchhotfix_212
still so you can also recover this version.
1 | git checkout master |
Rebase
Changing the order of commits
You can change the order of commits. Have a look at the recent history usinggit log --pretty=oneline
.
1 | a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c |
Where a is the first commit, and c the last one.
Using git rebase --interactive HEAD~2
, an interactive window opens. Note that
now, going down the list means going from old to newer commits (unilke above).
1 | pick b76d157 b |
You can now change the order of the commits by c/p the second line (commit c)
to the top and save and exit.
Squashing commits
This refers to merging multiple commits into one. Again, use git rebase -i
as
a starting point. First, reanrange the commits as described above. Squashing
means merging multiple commits into one. Say, if you wanted to squash commit
c into b, you had to change the following:
1 | pick b76d157 b |
Read this from top to bottom:
Hence, first commit b is picked (applied to the code base), then commit c is
added on top of it to form one commit with b. Finally, you get to rewrite the
commit message:
1 | # This is a combination of 2 commits. |
integrate upstream changes
Lets assume there is a repo on GitHub you want to contribute to. Since you cannot push
directly to it, you need to create a fork (on GitHub) first, clone this fork
to your machine and commit your changes to the fork, push and then submit a pull
request. Let us assume you are done with your changes and you pushed to the
fork but then you can’t merge with the upstream because in the meantime
(i.e. after you forked until now) there were some other people comitting to
the upstream branch and your fork is out of date. You can rebase your fork onto
the upstream, i.e. apply all commits that the upstream is ahead of your fork
to the fork and then, on the tip of this tree, apply your commits.
1 | git pull --rebase # or to be explicit |
Undo things
Go back to a previous version in the git repo
If you deleted a file in version control, please refer to the paragraph
recover deleted files.
Create a new branch
The best way to restore a previous stage is probably just by creating a new
branch with that stage indicated with a hashtag.
1 | git checkout -b 123keri5 |
Revert
Use revert
to create a new downstream commit that removes a specific
(upstram) commit. You are NOT destroying anything.
1 | git revert 123keri5 |
Cherry-pick
In some sense, cherry-pick is the opposite of git revert
. It allows you to
apply arbitrary commits from other branches to your current branch. You can also
specify multiple commits at once.
1 | git cherrypick e14jd 3kg05 # apply two commits to our current branch |
Reset
Use reset
to go back to a previous commit and delete all commits between the
latest commit and the one you want to go back to.
1 | # create a new commit equal to the hash of a previous commit. |
Recover deleted files or deleting files
In contrast to the situation where one wants to restore a previous version from
git, having deleted a file from git does not easily allow to restore it, since
it is no longer tracked.
Imagine the following situation:
- your file
example.txt
was tracked by git. - you decided to move the file into the folder
example
. The file is no longer
in the original directory. - now, you type
git status
and it says “deleted: ..” in red, meaning that it is
not yet stagged. - type
git checkout -- example.txt
to discard the removal and restore HEAD. - on the other hand, if you want to confirm the removal, type
git rm example.txt
. Note that, if you want to remove a whole directory, you
will need the-r
flag as a double check. - Now, after typing
git status
the “deleted: …” is displayed in green,
saying that changes are stagged. - To go back, you might type
git reset HEAD
(to unstage) and thengit checkout -- example.txt
to restore everything.
Inspecting differences
Changes between tracked files
####git log
git log
shows a log book with all commits
1 | git log # show the log / history of the git repository |
On a side note, you can also see the whole list of git commands (not only
commits) using git reflog
. For example, if you switched from one branch to
the other, it shows you where you switched to, but not where you switched
from. So if you forgot the branch you are coming from, just type git reflog
and you will be able to see a log of all commands and a comment.
git diff
git diff
is designed to highlight changes between two revisions or arbitrary
commits or files.
between HEAD and uncommitted changes
1 | git diff # inspect differences between HEAD and local file (= unstagged changes). |
between committed revisions
1 | git diff HEAD~2 HEAD # difference between current HEAD and the one two commits ago. |
between branches \
1 | # inspect all the differences between HEAD oftwo branches (master and branches). |
Differnces between untracked files
to inspect two files and see thir difference, you can also use git. The
respective files don’t even have to be tracked by git.
1 | # compare the two files a.do and b.do in the working directory. |
Tagging
Tags can be used to create a reference to a certain stage of the directory,
which we can think of as verisons. There are two kinds of tags:
- lightweith tags are simple tags.
- Annotated tags are tags with attributes (such as author, tagging message and
so on.
In contrast to hashes that refer to a commit on the tree of a file, tags
typically refer to more than one file. For example if three files make up a
functioning suite, then once all three work fine together, we might want to tag
the bundle of all current versions of the three files and be able to restore
them all togehter very easily.
1 | # pull tag. This happens by default with the following standard commands |
Remote Repositories
Cloned Repository
Get the repository
If the code you want to work with is in a remote repository, you need to get a
local copy before you can start. Here, we juse a repository calleda_git_test_folder
.
1 | # download a copy of the current code at github |
Inspect the repository
Let’s now look at the branches in this git repository. The first two commands
were introduced in this text further above.
1 | git branch # list you the (local) branches |
From looking at the terminal output, you can see that
- remote branches have a name starting with
remotes
. - by default, the cloned repository is called
origin
.origin
is actually
just a placeholder for the full url where the repository originates from. we
can give this repository an arbitrary name. - our cloned repository has different branches. These are the branches
available:- all the branches in the remote branch
- a branch called HEAD, which is a pointer to the branch that is the
current remote head
- remote branches are displayed in red when coloring is enabled
In contrast to what you saw before, this repository is “connected” to the
remote repository. This is also visible when typing git status
, where you
will see more information than if it was an “unconnected” local repository. The
added information for now here is that your local branch master is up to date
with the remote repository origin/master.
To see the remote “connections”, type the following command is your friend.
1 | git remote -v # show what origin points to |
There are always two directions of a remote repository, one is the one where
you fetch (download) code from, the other one is where you push (upload) your
committed work. In simple settings, the two are the same. You can also add new
remote commections like this:
1 | git remote add alternativeorigin https://github.com/lorwal/a_git_test_folder_alternative.git |
Use other than the master branch locally.
By default, you will be on the master branch. However, you might want to wok on
a different branch. All other remote branches that are shown withgit remote -va
are not local copies. To generate local copies, simply use
1 | git checkout --track feature2 |
To create a local copy of the remote branch feature2. The track
option means
that git tracks how the remote branch and the local branch differ, e.g. when
using git status
. Such a relationship between the local and remote master is
established automatically with git clone
. This tracking relationship also
allows us to use git fetch
, git pull
and git push
without further
instructions. This is in most cases the desired behaviour.
Push new local branch
Assume you branched out for a new feature (feature3) locally, and you want to
push the changes to github. If you want to push an entire branch that does not
yet exist in the remote repository, a simple git push
will fail. To add the
branch as a new remote branch, use
1 | git push --set-upstream origin feature3 |
If you intend to merge this new banch with another branch at github, go to
github and open a pull request. The owner of the repository can then decide
when to merge the proposed branches on github.
Alternatively, you can merge them locally and then push to the final branch,
e.g. merge with master
before you push to the master branch.
Configuring
You got global git configurations saved in ~/yourusername/.gitconfig
.
They can be altered using git config --global your_command
.
On the other hand, you can also set config options in a given directory -
without the global option. These configurations will then only apply to this
folder.
See [this link](http://stackoverflow.com/questions/4220416/can-i-specify-multiple-users-for-myself-in-gitconfig] for an extensive discussion.
Workflow
Basics
The basic workflow is as follows:
- you clone a repository before you start (as we just did).
- you commit changes to the local repository.
- After a few commits, the commits form a ‘bundle’ and you are ready to push.
Maybe, just in time, you find something else you want to change that actually
belongs to your latest commit. Use--amend
andno-edit
to modify the latest
commit. This is only possible before you push. - you use
git push
(or an explicit form, e.g.git push origin master
to
push the master branch to the corresponding remote branch). Do not wait too
long with pushing to avoid merge conflicts with other people. You need to pull
the latest version before you push.Just before you push your changes, you local
branch is “ahead” of the remote branch if the remote branch was not modified
since you cloned it. If we work on the master branch and make one commit, our
local branch will be “ahead of origin/master by n commits”, as you can read
when you typegit status
. - If other people are contributing, then your previously cloned repository gets
out of date. You have to download the latest version of the remote repository,
inspect changes and merge them with your repository before you can push them to
the remote repository. Usegit fetch
to update the copy of the remote
repository on your local disc. You can use tools likegit diff
orgit log
to compare the versions. At that stage, your local (unmodified) copy is said to
be “behind” the remote repository. If you are ready to merge the cahnges, usegit merge
to merge the copy of the up-to-date remote and your changes made
based on the old version of the remote.git pull
is the fast way to do that:
It executes the fetching and merging in one step. - Note that you cannot push code if it is based on an out-of-date repository.
Advanced
From: https://github.com/lorenzwalthert/w-c4ds/blob/master/01-learning_resources/command_line_git.Rmd