These are some very brief and imperfect notes on working with git and github. For more thorough information see:
- Basic commands
- Fetch and Merge
- Reset, Checkout, Revert
- Branch
- Setting the upstream branch
- Rebase
- Stashing
- Force Push and Pull
- .gitignore
- Commit messages
- Git and Github identity information
- Git remote origin on a flash key
- A Specific Process Example
- Miscellaneous commands
- GitHub code search
In the command line, make sure you're in the directory you want to repo first.
command | description |
---|---|
git init |
creates a new repo |
git add -A |
adds all files, (new, modified and deleted) to the staging area |
git add . |
adds all files, (new, modified) to the staging area |
git add filename.txt |
adds a specific file to the staging area |
git status |
shows you the staging area |
git reset filename.txt |
remove a staged file |
git commit |
takes you to your editor to write a commit comment. Once you save and close the file, the commit will complete |
git commit -m 'My comments' |
commits right away using the commit comments in quotes |
git commit --amend |
lets you include a file you forgot to include in the last commit (provided it hasn't been pushed yet). You need to add the file first with git add filename.txt . You can also run this command alone to make changes to your last commit message (again, provided it hasn't been pushed yet). |
git diff |
will show a basic diff of the modified files in the working directory |
git diff --staged |
show changes of a file in the staged area |
git diff <commit>^! |
show changes in a commit |
git difftools |
to look into - will do a diff using a better tool |
git log |
shows your commit log |
git log --oneline |
shows your commit log formatted as one line, with a shortened commithash |
git log --pretty=oneline |
shows your commit log formatted as one line with full length commithash |
git push |
pushes data from the local repository to the remote repository |
git fetch |
fetches data from the remote repository to the local repository |
git merge |
merges the data from the local repository into the working directory |
git pull |
(fetch + merge) fetches data from the remote repository to the local repository and then merges it to the working directory |
git remote -v |
show remote repository |
As noted above fetch
and merge
are two separate steps that make up a pull
. Most of the time pull
is all you need but doing these steps separately allows you to:
View a commits diffs before merging, for example:
git fetch
From https://github.com/jessicarush/git-notes
8b684ad..4f87800 master -> origin/master
git diff 4f87800^!
git merge
These three commands will let you temporarily look back at earlier commits, reset to a previous commit, remove commits from the log or revert individual files to previous commit states.
Reset is the simplest if you want to revert all your files to an earlier commit and/or remove commits from your commit history. You have two main options using reset:
command | description |
---|---|
git reset |
Resets the staging area to match the specified commit but keeps changes in the working directory. |
git reset --hard |
BE CAREFUL Resets the working directory and the staging area to match the current HEAD commit. After you have done this you can run git clean -fd to remove any untracked and directories. |
git reset [commit] |
Uses the default mode --mixed which removes all commits after [commit] in the history but preserves any changes made to your local files. If you do a git status , it will show as changes not staged for commit. |
git reset --soft [commit] |
The same as above except all changes since [commit] will be staged and waiting for a new commit. |
git reset --hard [commit] |
BE CAREFUL Removes all commits after [commit] AND reverts local files back to what they were at the specified [commit] |
git reset --hard HEAD~1 |
BE CAREFUL Goes back one commit. |
If you're not sure which commit you want to reset to, use git checkout
first. There are also mode flags --merge
and --keep
: see the docs.
While this is mainly used for switching branches, checkout
also lets you look at your files as they were at a particular [commit]. You can check out all your files or just a single file. The process is a little different depending on which you're doing. Note that unlike reset
, checkout does not remove any commits from the commit history.
git checkout [commit]
– temporarily reverts all local files to [commit]
At this point if you look at the actual files in your working directory you'll see that they're rolled back to [commit] but know this state is not permanent. You are in what's called a detached head state. You can do some things in this state, including running the code, test changes, checking out other commit states but ultimately, when you're done, you'll most likely do one of two things:
-
go back to your most recent commit like so:
git checkout master
–– this puts your head (and files) back to the end of the master branch. Once here, you may then decide you want togit reset
orgit revert
. -
create a new branch from this previous commit state with the intention of continuing to make changes and eventually merging back into the master branch. It might look something like this:
git checkout [commit]
git checkout -b mybranch
...making changes...
git add -A
git commit -m 'Added mybranch features'
git checkout master
git merge mybranch
Depending on your changes, you will likely have merge conflicts so you'll have to resolve them carefully before moving on. For this reason, consider whether you want to keep those later commits in the master branch that you moved in front of. It may make more sense to reset, then branch off.
git checkout [commit] filename.txt
– reverts a file to [commit]
The difference here when we just checkout a single file, is that we're NOT in a detached head state meaning it's pretty much business as usual. Your file has been reverted back to a previous commit so at this point you can do one of two things:
-
choose to keep the file at this earlier commit by doing a
git add
and agit commit
just as if you were making normal changes to a file. If you look at your log, you'll see all commits are in tact. So, you could always change your mind again. -
choose to go back to your most recent state by checking out using the most recent commit hash:
git checkout [commit] filename.txt
. If you do agit status
you should see no changes.
Note that when indicating a commit to checkout to, you can also use this syntax:
git checkout HEAD~2
– checkout two parent commits ago
git checkout HEAD~2 filename.txt
– checkout a file from two parent commits ago
You can also use ^
to differentiate between merged branch parents, but tbh I find it a little confusing. Here's an explanation but I'm sticking with commit hashes.
The git revert command is a forward-moving undo operation that offers a safe method of undoing changes. Instead of deleting or orphaning commits in the commit history, a revert will create a new commit that inverses the changes specified. Git revert is a safer alternative to git reset in regards to losing work.
git revert [commit]
When you run this command, your editor will open up for a commit message. You can leave it at the default which is 'revert: commit message'. Here's an example:
git revert 13978d2
git log --oneline
e679e71 Revert "Add three"
13978d2 Add three
0524870 Add two
d5d461c Add one
a114858 Initial commit
Create a new branch and switch over to it:
git branch new_features
git checkout new_features
Make your changes as you normally would, add
and commit
as normal.
To push your new branch to your remote repo:
git push -u origin new_features
If you want to rename a branch:
git branch -m old-name new-name
git push origin --delete old-name
git push origin new-name
When ready to merge your branch into the master branch:
git checkout master
git merge new_features
git push
To list all your branches (local & remote):
git branch -a
To delete a local branch:
git branch -d branch_name
To delete a remote branch:
git push origin --delete branch_name
Summary:
command | description |
---|---|
git branch refactoring |
creates a new branch called 'refactoring' |
git checkout refactoring |
moves over to the new branch and updates the working directory |
git checkout -b refactoring |
creates a new branch and move over to it in one step. |
git push --set-upstream origin refactoring |
add your new branch to the the remote repo |
git pull origin refactoring |
to pull from a branch |
git branch |
display the names of all the branches (the starred one is your current one) |
git merge refactoring |
merge the branch into the branch you are currently in (for example: git checkout master first) |
Note that in the command used above git push -u <remote> <branch>
, we are actually setting the upstream branch to <remote>
in addition to pushing.
Upstream branches are useful because:
- You get references to your remote repositories and you essentially know if you are ahead of them or not. When performing a “git fetch” command, you can bring the new commits from your remote repository and you can choose to merge them at will.
- You can perform pull and push easily. When you set your upstream (or tracking) branches, you can simply execute pulls and pushes without having to specify the target branch.
To set the upstream:
git branch -u <remote>/<branch>
To check what the upstreams have been set to for each branch:
git branch -vv
coming soon...
git pull --rebase
| instead of performing a regular fetch
+ merge
, this will do a fetch
+ rebase
If you have un-staged, un-committed changes that you want to "save for later", you can stash them.
git stash save "my description"
To list your saved stashes:
git stash list
To show the diff of the most recent stash before applying:
git stash show -p
To show the diff of a specific stash before applying:
git stash show -p stash@{1}
To retrieve at stash:
git stash apply stash@{0}
The stash will still be saved though. If you want to remove it from the saved list:
git stash drop stash@{0}
To remove all the saved stashes:
git stash clear
To apply and remove the last stash:
git stash pop
To force a pull (and overwrite local diffs):
git fetch --all
git reset --hard origin/master
To force a push (and overwrite remote diffs)
git push origin <your_branch_name> --force
To have git ignore files or folders, create a file in the project root directory (where your .git
folder lives) called .gitignore
– then add a line for each file or folder you want to ignore, for example:
# https://git-scm.com/docs/gitignore
# https://help.github.com/articles/ignoring-files/
# dependencies
/node_modules
# bytecode compiled
__pycache__/
# environments
.env
/venv
# logs
/logs
# database
app.db
# misc
.DS_Store
# project specific
working_files
Note that:
# ignore all .a files
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the venv dir/file in the current directory, not subdir/venv
/venv
# ignore all files in any directory named __pycache__
__pycache__/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf files in the doc/ directory and any of its subdirectories
doc/**/*.pdf
Note if you add a new file to your git ignore that was previously being tracked, you will need to remove it from the index:
git rm --cached <file>
If you want to remove a whole directory, you need to remove all files in it recursively:
git rm -r --cached <directory>
To learn more about the patterns that can be used for .gitignore
files see:
- (GitHub ignoring files)[https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files]
- (Pro git book)[https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#_ignoring]
- (Git man pages)[https://git-scm.com/docs/gitignore]
The preferred method of writing commit messages is:
Capitalized, short (50 chars or less) summary
More detailed explanation, if necessary. Keep line length to about 72
characters. Write your commit message in the imperative: "Fix bug" and
not "Fixed bug" or "Fixes bug." This convention matches up with commit
messages generated by commands like git merge and git revert. Explain
your solution and why you're doing it, as opposed to describing what
you're doing.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet,
preceded by a single space, with blank lines in
between, but conventions vary here
Note that keeping the first subject line to 50 chars or less works well with: git log --pretty=oneline
Some further recommendations for writing commit messages:
How to Write a Git Commit Message
Commit Message Guidelines
Some projects also include a short reference word at the start of the summary line to indicate the type of change. An example of this is seen in the pandas repo.
An example system I could get behind:
Feature: a new feature
Fix: a bug fix
Docs: documentation changes only
Style: changes that do not affect the meaning of the code (e.g. white-space, formatting, etc)
Refactor: a code change that neither fixes a bug nor adds a feature
Perf: a code change that improves performance
Test: adding or modifying tests
Update: changes related to updating external tools/libraries
For my notes & examples repos, I'm also going to use:
Add: new examples, explanations or material added.
Rename: a file has been renamed to better reflect the topic
If you made a mistake in your last commit message and you haven't pushed yet, you can use the --amend flag noted above. If you notice mistakes in old commit messages do this:
- Use
git rebase -i HEAD~n
command to display a list of the last n commits in your default text editor. - For each commit message you want to edit, change the word pick to reword then save and close the file.
- This will then open each commit message file for you to edit and save.
- When it's all done, force push the amended commits with
git push --force
Be aware that amending the commit message will result in a new commit with a new ID. For more information see Github's article
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
Add your Github name and email address (note: your email here must match the primary one you have set on your Github account in order for your commits to register on their heatmap):
git config --global user.email "email@example.com"
git config --global user.name "Your Name"
To confirm that you have set the email address correctly:
git config --global user.email
email@example.com
If you want to change your name and email for a local repository only, navigate to the directory containing the git directory:
git config user.email "email@example.com"
git config user.name "Your Name"
To confirm that you have set the email address correctly:
git config user.email
email@example.com
To show the remote repository connected to your current working directory:
git remote show origin
To create a clone of a github repository:
git clone https://github.com/USERNAME/REPOSITORY
If you've added your public SSH keys to your github account:
git clone git@github.com:USERNAME/REPOSITORY
To link an existing local repo to a github repo:
git remote add origin https://github.com/USERNAME/REPOSITORY.git
If you've added your public SSH keys to your github account:
git remote add origin git@github.com:USERNAME/REPOSITORY.git
Note: if you want to change the origin, edit the config file in the .git
directory or you can run the command to delete the old origin, then add the new one with above command:
git remote remove origin
Be sure to push to your master branch after changing the remote:
git push -u origin master
The above requires that you have a master branch already created on github. If not you'll need to enter the following. You'll be prompted for a username and password (unless you set up SSH).
git push --set-upstream origin master
Finally, if you are trying to interact with a github repo and are still being asked to enter your username and password, you probably need to change the remote URL from HTTPS to SSH. To do this first check if you are using HTTPS:
git remote -v
> origin https://github.com/USERNAME/REPOSITORY.git (fetch)
> origin https://github.com/USERNAME/REPOSITORY.git (push)
Change your remote's URL from HTTPS to SSH with the git remote set-url
command.
git remote set-url origin git@github.com:USERNAME/REPOSITORY.git
Verify that it's changed:
git remote -v
> origin git@github.com:USERNAME/REPOSITORY.git (fetch)
> origin git@github.com:USERNAME/REPOSITORY.git (push)
You can test that SSH is set up properly to Github with this command:
ssh -T git@github.com
- git init, add and commit a repo in your local directory
- cd into the USB flash key (e.g.
cd /Volumes/flash_key_name/Python
) git init --bare myrepo.git
- cd back to local repo
git push --set-upstream /Volumes/flash_key_name/Python/myrepo.git/ master
git remote add usb /Volumes/flash_key_name/Python/myrepo.git
git push usb master
To clone that repo somewhere else:
git clone /Volumes/flash_key_name/Python/myrepo.git
Different organizations will have their own preferred methods of managing git logs. This example is one such method aimed at doing pull requests of 1 commit.
git fetch
git checkout origin/master
git checkout -b CP-123-wip
git branch -u origin/master
Do work and commit as much as you want (no pushing unless you want a backup), then:
git pull
git status
# ahead by x commits
git rebase -i HEAD~x
Note if you were ahead by more than one commit, the rebase command will prompt you amend the commit message so that you can write it in the organizations preferred format e.g.
CP-123: This is my formal commit message
. If you were only ahead by 1 commit in the first place, don't bother rebasing but do amend your last commit message if necessary usinggit commit --amend
.
In the interactive rebase
pick
the first one andsquash
the rest, the second step of the rebase will allow you to merge and modify the commit message.
git status
# ahead by 1 commit
git push origin HEAD:CP-123
git push
:
- Relies on the upstream configuration.
- Pushes changes to the branch that the current branch is tracking.
- Simplifies the workflow by reducing the need for additional specification.
git push origin HEAD:feature
:
- Explicitly specifies the source (HEAD, which is your current branch) and the destination (feature on origin). If you weren't currently on the branch you could do
git push origin dev_feature:feature
. - Can be used to push to a branch that may not be the configured upstream.
- Useful for creating new branches on the remote or when upstream is not set.
In the above situation if the master branch has some features merged while you are working on your branch then you will need to pull down the new stuff and possibly resolve any conflicts before cherry-picking your work into a new commit:
git fetch
git checkout origin/master
git checkout -b CP-123-wip
git branch -u origin/master
Do work and commit as much as you want (no pushing unless you want a backup), then:
git checkout master
git pull
git checkout -b CP-123-wip
git status
# diverged, x commits on master and x commits on branch
git rebase -i HEAD~x # where x is number of commits on branch
git status
# diverged, x commits on master and 1 commit on branch
At this point, note the commit hash of the rebased branch. Then:
git checkout -b CP-123
git branch -u origin/master
git cherry-pick <hash>
At this point you may have conflicts. Resolve them by doing:
git status
Open the files and manually resolve any conflict sections:
<<<<<<< HEAD
open an issue
=======
ask your question in IRC.
>>>>>>> branch-a
Once you've resolves a file, save it and add it (git add filename
). When all files are added, commit.
Your new branch should now be one commit off the current master branch.
git push origin HEAD:CP-123
Assuming I've created a local branch using:
git checkout -b CP-123-wip
And I'm following the master branch:
git branch -u origin/master
If I want to test my current local branch's code on a raspberry pi:
git push origin HEAD:CP-123-wip
On the raspberry pi, clone the repository, then:
git checkout origin/CP-123-wip
If I want to make changes there on the pi, commit as normal then when ready:
git push origin HEAD:CP-123-wip
Now locally you should be able to pull down those changes:
git pull origin CP-123-wip
You can carry on editing back and forth between your local workspace and the pi using those two commands. When you're ready for a pull request, continue as normal with rebasing, etc.
When copying files from linux or Mac OS to Windows, permissions on files may get changed. Git may register these as changes and all files will show as being modified. If you don't want this use the following command:
git config core.filemode false
or
git config --global core.filemode false
To print git log with author, date, commit message:
git log --pretty=format:"%h%x09%an%x09%ad%x09%s"
Gives some detail into the files that were modified:
git whatchanged
Show the amount each file was changed:
git log --stat
git diff --stat <sha1> <sha2>
gives the files and the amount of changes between two commits.
git diff --stat <branch>
to compare to another branch (e.g. master)
Compare Two Git Branches:
git diff <branch>...<branch> --ignore-all-space
To rename main
to master
:
git branch -m main master
git push -u origin master
If you already have a github repo, you'll want to go into the repo settings and manually set master as the default branch then delete the main branch.
GitHub now includes all kinds of syntax for specific code searches. Here are some examples:
search query | description |
---|---|
kysely path:**/package.json | search for kysely in any package.json . |