- Git Homepage
- Installation
- Git Configuration
- Info Commands
- Setting up a repository
- Saving changes
- Undoing Changes
- Branches
- Synchronize changes
- git push
macOS by default comes with git
and is part of the operating system. You DO NOT want to un-install it.
If you want a different version, install it using brew using below command:
$ brew install git
Homebrew (brew) will take care of all dependencies and configure the latest Git version, however there is still a chance it will find a setting it cannot resolve automatically, so read what Homebrew says at the end.
For optional GUI application
$ brew install git-gui
Using winget
tool:
winget install --id Git.Git -e --source winget
Links to Standalone or Portable installer can be found at https://git-scm.com/downloads/win.
https://desktop.github.com/download/
Run below command to check installed version:
$ git --version
Command | Description |
---|---|
git config --global user.name "[name]" |
Sets the name you want attached to your commit transactions |
git config --global user.email "[email address]" |
Sets the email you want attached to your commit transactions |
git config --global color.ui auto |
Enables helpful colorization of command line output |
$ git config --global core.excludesFile ~/.gitignore_global |
Set global .gitignore file.Be sure to create .~/gitignore_global file before executing this command. |
Many Git commands will launch a text editor to prompt for further input. One of the most common use cases for git config
is configuring which editor Git should use. Listed below is a table of popular editors and matching git config
commands:
Editor | config command |
---|---|
Atom | $ git config --global core.editor "atom --wait" |
emacs | $ git config --global core.editor "emacs" |
nano | $ git config --global core.editor "nano -w" |
vim | $ git config --global core.editor "vim" |
Sublime Text (Mac) | $ git config --global core.editor "subl -n -w" |
Sublime Text (Win, 32-bit install) | $ git config --global core.editor "'c:/program files (x86)/sublime text 3/sublimetext.exe' -w" |
Sublime Text (Win, 64-bit install) | $ git config --global core.editor "'c:/program files/sublime text 3/sublimetext.exe' -w" |
Textmate | $ git config --global core.editor "mate -w" |
Git aliases are a powerful workflow tool that create shortcuts to frequently used Git commands. Using Git aliases will make you a faster and more efficient developer. Aliases can be used to wrap a sequence of Git commands into new faux Git command. Git aliases are created through the use of the git config command which essentially modifies local or global Git config files.
To better understand Git aliases let us create some examples.
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
On linux systems, the global config file is located in the User home directory at /.gitconfig
.
[alias]
co = checkout
br = branch
ci = commit
st = status
A common Git pattern is to remove recently added files from the staging area. This is achieved by leveraging options to the git reset
command. A new alias can be created to encapsulate this behavior and create a new alias-command-keyword which is easy to remember:
$ git config --global alias.unstage 'reset HEAD --'
The preceding code example creates a new alias unstage
. This now enables the invocation of git unstage. git unstage
which will perform a reset on the staging area. This makes the following two commands equivalent.
$ git unstage fileA
$ git reset HEAD -- fileA
The git status
command displays the state of the working directory and the staging area. It lets you see which changes have been staged, which haven’t, and which files aren’t being tracked by Git. Status output does not show you any information regarding the committed project history. For this, you need to use git log
.
The git log
command displays committed snapshots. It lets you list the project history, filter it, and search for specific changes. While git status
lets you inspect the working directory and the staging area, git log
only operates on the committed history.
Log output can be customized in several ways, from simply filtering commits to displaying them in a completely user-defined format. Some of the most common configurations of git log
are presented below.
-
Display the entire commit history using the default formatting. If the output takes up more than one screen, you can use Space to scroll and q to exit.
$ git log
-
Limit the number of commits by . For example, git log -n 3 will display only 3 commits. Condense each commit to a single line. This is useful for getting a high-level overview of the project history.
$ git log -n <limit>
-
Along with the ordinary git log information, include which files were altered and the relative number of lines that were added or deleted from each of them.
$ git log --oneline $ git log --stat
-
Display the patch representing each commit. This shows the full diff of each commit, which is the most detailed view you can have of your project history.
$ git log -p
-
Search for commits by a particular author. The <pattern> argument can be a plain string or a regular expression.
$ git log --author="<pattern>"
-
Search for commits with a commit message that matches <pattern>, which can be a plain string or a regular expression.
$ git log --grep="<pattern>"
-
Show only commits that occur between < since > and < until >. Both arguments can be either a commit ID, a branch name, HEAD, or any other kind of revision reference.
$ git log <since>..<until>
-
Only display commits that include the specified file. This is an easy way to see the history of a particular file.
$ git log <file>
-
A few useful options to consider. The --graph flag that will draw a text based graph of the commits on the left hand side of the commit messages. --decorate adds the names of branches or tags of the commits that are shown. --oneline shows the commit information on a single line making it easier to browse through commits at-a-glance.
$ git log --graph --decorate --oneline
git show
is a command line utility that is used to view expanded details on Git objects such as blobs, trees, tags, and commits. git show
has specific behavior per object type.
Tags show the tag message and other objects included in the tag. Trees show the names and content of objects in a tree. Blobs show the direct content of the blob. Commits show a commit log message and a diff output of the changes in the commit.
Git objects are all accessed by references. By default, git show
acts against the HEAD reference. The HEAD reference always points to the last commit of the current branch. Therefore, you can use git show
to display the log message and diff output of the latest commit.
-
<object>…
A reference to an object or a list of objects may be passed to examine those specific objects. If no explicit objects are passed,
git-show
defaults to the HEAD reference. -
--pretty[=<format>]
The pretty option takes a secondary format value that can be one of:
oneline, short, medium, full, fuller, email, raw,
andformat:<string>
. If omitted, the format defaults tomedium
. Each format option is a different template for how Git formats the show output. The <code>oneline</code> option can be very helpful for showing a list of commits -
--abbrev-commit
This option shortens the length of output commit IDs. Commit IDs are 40 characters long and can be hard to view on narrow terminal screens. This option combined with
--pretty=oneline
can produce a highly succinctgit log
output. -
--no-abbrev-commit
Always Show the full 40 character commit ID. This will ignore --
abbrev-commit
and any other options that abbreviate commit IDs like the--oneline format
-
--oneline
This is a shortcut for using the expanded command
--pretty=oneline --abbrev-commit
-
--encoding[=<encoding>]
Character encoding on Git log messages defaults to UTF-8. The encoding option can change to a different character encoding output. This is useful if you are working with Git in an environment with different character encoding, like an Asian language terminal.
-
>--expand-tabs=<n> --expand-tabs --no-expand-tabs
These options replace tab characters with spaces in the log message output. The
n
value can be set to configure how many space characters the tabs expand to. Without an explicit n value tabs will expand to 8 spaces.--no-expand-tabs
is equivalent ton=0
-
--notes=<ref> --no-notes
Git has a note system that enables arbitrary ‘note’ metadata to be attached to objects. This data can be hidden or filtered when using
git-show
. -
--show-signature
This option will validate the commit is signed with an encrypted signature by passing it to a gpg subcommand.
The --pretty
option discussed above accepts several secondary options to massage the format of git-show
output. These secondary options are listed below with example template
-
oneline
<sha1> <title line>
Oneline attempts to compact as much info into a single line as possible
-
short
commit <sha1> Author: <author> <title line>
-
medium
commit <sha1> Author: <author> Date: <author date> <title line> <full commit message>
-
full
commit <sha1> Author: <author> Commit: <committer> <title line> <full commit message>
-
fuller
commit <sha1> Author: <author> AuthorDate: <author date> Commit: <committer> CommitDate: <committer date> <title line> <full commit message>
-
email
From <sha1> <date> From: <author> Date: <author date> Subject: [PATCH] <title line> <full commit message>
-
raw
raw format ignores other direct formatting options passed to
git-show
and outputs the commit exactly as stored in the object. Raw will disregard--abrev
and--no-abbrev
and always show the parent commits. -
format:
format enables the specification of a custom output format. It works similar to the C language’s
printf
command. The--pretty=format
option takes a secondary value of a template string. The template has access to placeholder variables that will be filled with data from the commit object. These placeholders are listed below:Placeholder Description %H commit hash %h abbreviated commit hash %T tree hash %t abbreviated tree hash %P parent hashes %p abbreviated parent hashes %an author name %aN author name %ae author email %aE author email %ad author date (format respects --date= option) %aD author date, RFC2822 style %ar author date, relative %at author date, UNIX timestamp %ai author date, ISO 8601 format %cn committer name %cN committer name %ce committer email %cE committer email %cd committer date %cD committer date, RFC2822 style %cr committer date, relative %ct committer date, UNIX timestamp %ci committer date, ISO 8601 format %d ref names, like the --decorate option of git-log(1) %e encoding %s subject %f sanitized subject line, suitable for a filename %b body %N commit notes %gD reflog selector, e.g., refs/stash@{1} %gd shortened reflog selector, e.g., stash@{1} %gs reflog subject %Cred switch color to red %Cgreen switch color to green %Cblue switch color to blue %Creset reset color %C(...) color specification, as described in color.branch.* config option %m left, right or boundary mark %n newline %% a raw % %x00 print a byte from a hex code %w([[,[,]]]) switch line wrapping, like the -w option of git-shortlog
$ git show --pretty="" --name-only bd61ad98
This will list all the files that were touched in a commit
$ git show REVISION:path/to/file
This will show a specific version of a file. Replace the REVISON
with a Git sha.
$ git show v2.0.0 6ef002d74cbbc099e1063728cab14ef1fc49c783
This will show the v2.0.0 tag and also commit at 6ef002d74cbbc099e1063728cab14ef1fc49c783
$ git show commitA...commitD
This will output all commits in the range from commitA
to commit D
git diff
is a multi-use Git command that when executed runs a diff function on Git data sources. These data sources can be commits, branches, files and more. The git diff
command is often used along with git status
and git log
to analyze the current state of a Git repo.
To understand this command, consider below example:
$ git log --oneline
460740c (HEAD -> master) reverting back to fourth
0a4a799 restore to third change
c922b14 fourth change
efae9f8 third change
335a181 second change
1976f2d (tag: v1.0) first change
8eca64f empty file
$ git diff efae9f8 c922b14
diff --git a/demo_file b/demo_file
index ed585a5..d81c4f3 100644
--- a/demo_file
+++ b/demo_file
@@ -1,3 +1,4 @@
first change
second change
third change
+fourth change
1. Comparison input
diff --git a/demo_file b/demo_file
This line displays the input sources of the diff. We can see that a/demo_file
and b/demo_file
have been passed to the diff.
2. Meta data
index ed585a5..d81c4f3 100644
This line displays some internal Git metadata. You will most likely not need this information. The numbers in this output correspond to Git object version hash identifiers.
3. Markers for changes
--- a/demo_file
+++ b/demo_file
These lines are a legend that assigns symbols to each diff input source. In this case, changes from a/demo_file
are marked with a ---
and the changes from b/demo_file
are marked with the +++
symbol.
4. Diff chunks
The remaining diff output is a list of diff 'chunks'. A diff only displays the sections of the file that have changes. In our current example, we only have one chunk as we are working with a simple scenario. Chunks have their own granular output semantics.
@@ -1,3 +1,4 @@
first change
second change
third change
+fourth change
The first line is the chunk header. Each chunk is prepended by a header enclosed within @@
symbols. The content of the header is a summary of changes made to the file. In our simplified example, we have -1 +1 meaning line one had changes. In a more realistic diff, you would see a header like:
@@ -34,6 +34,8 @@
In this header example, 6 lines have been extracted starting from line number 34. Additionally, 8 lines have been added starting at line number 34.
The remaining content of the diff chunk displays the recent changes. Each changed line is prepended with a +
or -
symbol indicating which version of the diff input the changes come from. As we previously discussed, -
indicates changes from the a/demo_file
and + indicates changes from b/demo_file
.
The git diff
command can be passed an explicit file path option. When a file path is passed to git diff
the diff operation will be scoped to the specified file. The below examples demonstrate this usage.
$ git diff HEAD ./path/to/file
This example is scoped to ./path/to/file
when invoked, it will compare the specific changes in the working directory, against the index, showing the changes that are not staged yet. By default git diff
will execute the comparison against HEAD
. Omitting HEAD
in the example above git diff ./path/to/file
has the same effect.
$ git diff --cached ./path/to/file
When git diff
is invoked with the --cached
option the diff will compare the staged changes with the local repository. The --cached
option is synonymous with --staged
.
Invoking git diff
without a file path will compare changes across the entire repository. The above, file specific examples, can be invoked without the ./path/to/file
argument and have the same output results across all files in the local repo.
git diff
can be passed Git refs to commits to diff. Some example refs are, HEAD
, tags, and branch names. Every commit in Git has a commit ID which you can get when you execute GIT LOG
. You can also pass this commit ID to git diff
.
$ git log --pretty=oneline
957fbc92b123030c389bf8b4b874522bdf2db72c add feature
ce489262a1ee34340440e55a0b99ea6918e19e7a rename some classes
6b539f280d8b0ec4874671bae9c6bed80b788006 refactor some code for feature
646e7863348a427e1ed9163a9a96fa759112f102 add some copy to body
$:> git diff 957fbc92b123030c389bf8b4b874522bdf2db72c ce489262a1ee34340440e55a0b99ea6918e19e7a
Branches are compared like all other ref inputs to git diff
$ git diff branch1..other-feature-branch
This example introduces the dot operator. The two dots in this example indicate the diff input is the tips of both branches. The same effect happens if the dots are omitted and a space is used between the branches. Additionally, there is a three dot operator:
$ git diff branch1...other-feature-branch
The three dot operator initiates the diff by changing the first input parameter branch1
. It changes branch1
into a ref of the shared common ancestor commit between the two diff inputs, the shared ancestor of branch1
and other-feature-branch. The last parameter input parameter remains unchanged as the tip of other-feature-branch.
To compare a specific file across branches, pass in the path of the file as the third argument to git diff
$ git diff main new_branch ./diff_test.txt
The high-level function of git blame
is the display of author metadata attached to specific committed lines in a file. This is used to examine specific points of a file's history and get context as to who the last author was that modified the line. This is used to explore the history of specific code and answer questions about what, how, and why the code was added to a repository.
git blame
only operates on individual files. A file-path is required for any useful output. The default execution of git blame
will simply output the commands help menu. For this example, we will operate on sample README.MD file.
$ git blame README.MD
Executing the above command will give us our first sample of blame output. Consider the following output is a subset of the full blame output of the README. Additionally, this output is static is reflective of the state of the repo at the time of this writing.
$ git blame README.md
82496ea3 (kevzettler 2018-02-28 13:37:02 -0800 1) # Git Blame example
82496ea3 (kevzettler 2018-02-28 13:37:02 -0800 2)
89feb84d (Albert So 2018-03-01 00:54:03 +0000 3) This repository is an example of a project with multiple contributors making commits.
82496ea3 (kevzettler 2018-02-28 13:37:02 -0800 4)
82496ea3 (kevzettler 2018-02-28 13:37:02 -0800 5) The repo use used elsewhere to demonstrate `git blame`
82496ea3 (kevzettler 2018-02-28 13:37:02 -0800 6)
89feb84d (Albert So 2018-03-01 00:54:03 +0000 7) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod TEMPOR incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
89feb84d (Albert So 2018-03-01 00:54:03 +0000 8)
eb06faed (Juni Mukherjee 2018-03-01 19:53:23 +0000 9) Annotates each line in the given file with information from the revision which last modified the line. Optionally, start annotating from the given revision.
eb06faed (Juni Mukherjee 2018-03-01 19:53:23 +0000 10)
548dabed (Juni Mukherjee 2018-03-01 19:55:15 +0000 11) Creating a line to support documentation needs for git blame.
548dabed (Juni Mukherjee 2018-03-01 19:55:15 +0000 12)
548dabed (Juni Mukherjee 2018-03-01 19:55:15 +0000 13) Also, it is important to have a few of these commits to clearly reflect the who, the what and the when. This will help Kev get good screenshots when he runs the git blame on this README.
This is a sample of the first 13 lines of the README.md file. To better understand this output lets break down a line. The following table displays the content of line 3 and the columns of the table indicate the column content.
Id | Author | Timestamp | Line Number | Line Content |
---|---|---|---|---|
89feb84d | Albert So | 2018-03-01 00:54:03 +0000 | 3 | This repository is an example of a project with multiple contributors making commits. |
If we review the blame output list, we can make some observations. There are three authors listed. In addition to the project's maintainer Kev Zettler, Albert So, and Juni Mukherjee are also listed. Authors are generally the most valuable part of git blame
output. The timestamp column is also primarily helpful. What the change was is indicated by line content column.
$ git blame -L 1,5 README.md
The -L
option will restrict the output to the requested line range. Here we have restricted the output to lines 1 through 5.
$ git blame -e README.md
The -e
option shows the authors email address instead of username.
$ git blame -w README.md
The -w
option ignores whitespace changes. If a previous author has modified the spacing of a file by switching from tabs to spaces or adding new lines this, unfortunately, obscures the output of git blame
by showing these changes.
$ git blame -M README.md
The -M
option detects moved or copied lines within in the same file. This will report the original author of the lines instead of the last author that moved or copied the lines.
$ git blame -C README.md
The -C
option detects lines that were moved or copied from other files. This will report the original author of the lines instead of the last author that moved or copied the lines.
While git blame
displays the last author that modified a line, often times you will want to know when a line was originally added. This can be cumbersome to achieve using git blame
. It requires a combination of the -w
, -C
, and -M
options. It can be far more convenient to use the git log
command.
To list all original commits in-which a specific code piece was added or modified execute git log
with the -S
option. Append the -S
option with the code you are looking for. Let's take one of the lines from the README output above to use as an example. Let us take the text "CSS3D and WebGL renderers" from Line 12 of the README output.
$ git log -S"CSS3D and WebGL renderers." --pretty=format:'%h %an %ad %s'
e339d3c85 Mario Schuettel Tue Oct 13 16:51:06 2015 +0200 reverted README.md to original content 509c2cc35 Daniel Tue Sep 8 13:56:14 2015 +0200 Updated README cb20237cc Mr.doob Mon Dec 31 00:22:36 2012 +0100 Removed DOMRenderer. Now with the CSS3DRenderer it has become irrelevant.
This output shows us that content from the README was added or modified 3 times by 3 different authors. It was originally added in commit cb20237cc by Mr.doob. In this example, git log
has also been prepended with the --pretty-format
option. This option converts the default output format of git log
into one that matches the format of git log
.
$ git init
The git init
command creates a new Git repository. It can be used to convert an existing, unversioned project to a Git repository or initialize a new, empty repository. Most other Git commands are not available outside of an initialized repository, so this is usually the first command you'll run in a new project.
Executing git init
creates a .git
subdirectory in the current working directory, which contains all of the necessary Git metadata for the new repository. This metadata includes subdirectories for objects, refs, and template files. A HEAD file is also created which points to the currently checked out commit.
If you've already run git init
on a project directory and it contains a .git
subdirectory, you can safely run git init
again on the same project directory. It will not override an existing .git
configuration.
$ git clone [url]
git clone
is primarily used to point to an existing repo and make a clone or copy of that repo at in a new directory, at another location. The original repository can be located on the local filesystem or on remote machine accessible supported protocols. The git clone
command copies an existing Git repository. This is sort of like SVN checkout, except the “working copy” is a full-fledged Git repository—it has its own history, manages its own files, and is a completely isolated environment from the original repository.
As a convenience, cloning automatically creates a remote connection called "origin" pointing back to the original repository. This makes it very easy to interact with a central repository. This automatic connection is established by creating Git refs to the remote branch heads under refs/remotes/origin
and by initializing remote.origin.url
and remote.origin.fetch
configuration variables.
The example below demonstrates how to obtain a local copy of a central repository stored on a server accessible at example.com
using the SSH username john:
$ git clone ssh://[email protected]/path/to/my-project.git
$ cd my-project
# Start working on the project
git clone <repo> <directory>
Clone the repository located at <repo>
into the folder called ~<directory>!
on the local machine.
git clone --branch <tag> <repo>
Clone the repository located at <repo>
and only clone the ref for <tag>
.
git clone -depth=1 <repo>
Clone the repository located at <repo>
and only clone the history of commits specified by the option depth=1. In this example a clone of <repo>
is made and only the most recent commit is included in the new cloned Repo. Shallow cloning is most useful when working with repos that have an extensive commit history. An extensive commit history may cause scaling problems such as disk space usage limits and long wait times when cloning. A Shallow clone can help alleviate these scaling issues.
$ git add .
$ git commit -m "[descriptive message]"
The commands: git add
, git status
, and git commit
are all used in combination to save a snapshot of a Git project's current state.
The git add
command adds a change in the working directory to the staging area. It tells Git that you want to include updates to a particular file in the next commit. However, git add
doesn't really affect the repository in any significant way—changes are not actually recorded until you run git commit
.
In conjunction with these commands, you'll also need git status
to view the state of the working directory and the staging area.
git add <file>
Stage all changes in <file>
for the next commit.
git add <directory>
Stage all changes in <directory>
for the next commit.
git add -p
Begin an interactive staging session that lets you choose portions of a file to add to the next commit. This will present you with a chunk of changes and prompt you for a command. Use y
to stage the chunk, n
to ignore the chunk, s
to split it into smaller chunks, e
to manually edit the chunk, and q
to exit.
To create an initial commit of the current directory, use the following two commands:
$ git add .
$ git commit
Once you’ve got your project up-and-running, new files can be added by passing the path to git add
:
$ git add hello.py
$ git commit
The above commands can also be used to record changes to existing files. Again, Git doesn’t differentiate between staging changes in new files vs. changes in files that have already been added to the repository.
The git commit
command captures a snapshot of the project's currently staged changes. Committed snapshots can be thought of as “safe” versions of a project—Git will never change them unless you explicitly ask it to.
Prior to the execution of git commit
, the git add
command is used to promote or 'stage' changes to the project that will be stored in a commit. These two commands git commit
and git add
are two of the most frequently used.
$ git commit
Commit the staged snapshot. This will launch a text editor prompting you for a commit message. After you’ve entered a message, save the file and close the editor to create the actual commit.
$ git commit -a
Commit a snapshot of all changes in the working directory. This only includes modifications to tracked files (those that have been added with git add
at some point in their history).
$ git commit -m "commit message"
A shortcut command that immediately creates a commit with a passed commit message. By default, git commit
will open up the locally configured text editor, and prompt for a commit message to be entered. Passing the -m
option will forgo the text editor prompt in-favor of an inline message.
$ git commit -am "commit message"
A power user shortcut command that combines the -a
and -m
options. This combination immediately creates a commit of all the staged changes and takes an inline commit message.
$ git commit --amend
This option adds another level of functionality to the commit command. Passing this option will modify the last commit. Instead of creating a new commit, staged changes will be added to the previous commit. This command will open up the system's configured text editor and prompt to change the previously specified commit message.
The following example assumes you’ve edited some content in a file called hello.py
on the current branch, and are ready to commit it to the project history. First, you need to stage the file with git add
, then you can commit the staged snapshot.
$ git add hello.py
This command will add hello.py
to the Git staging area. We can examine the result of this action by using the git status
command.
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: hello.py
The green output new file: hello.py
indicates that hello.py
will be saved with the next commit. From the commit is created by executing:
$ git commit
This will open a text editor (customizable via git config
) asking for a commit log message, along with a list of what’s being committed:
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch main
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
#modified: hello.py
Git doesn't require commit messages to follow any specific formatting constraints, but the canonical format is to summarize the entire commit on the first line in less than 50 characters, leave a blank line, then a detailed explanation of what’s been changed. For example:
Change the message displayed by hello.py
- Update the sayHello() function to output the user's name
- Change the sayGoodbye() function to a friendlier message
It is a common practice to use the first line of the commit message as a subject line, similar to an email. The rest of the log message is considered the body and used to communicate details of the commit change set. Note that many developers also like to use the present tense in their commit messages. This makes them read more like actions on the repository, which makes many of the history-rewriting operations more intuitive.
To continue with the hello.py
example above. Let's make further updates to hello.py
and execute the following:
$ git add hello.py
$ git commit --amend
This will once again, open up the configured text editor. This time, however, it will be pre-filled with the commit message we previously entered. This indicates that we are not creating a new commit, but editing the last.
Tags are ref's that point to specific points in Git history. Tagging is generally used to capture a point in history that is used for a marked version release (i.e. v1.0.1).
A tag is like a branch that doesn’t change. Unlike branches, tags, after being created, have no further history of commits.
To create a new tag execute the following command:
$ git tag <tagname>
Replace <tagname>
with a semantic identifier to the state of the repo at the time the tag is being created. A common pattern is to use version numbers like git tag v1.4
.
Git supports two different types of tags, annotated and lightweight tags. The previous example created a lightweight tag. Lightweight tags and Annotated tags differ in the amount of accompanying meta data they store. A best practice is to consider Annotated tags as public, and Lightweight tags as private. Annotated tags store extra meta data such as: the tagger name, email, and date. This is important data for a public release. Lightweight tags are essentially 'bookmarks' to a commit, they are just a name and a pointer to a commit, useful for creating quick links to relevant commits.
Annotated tags are stored as full objects in the Git database. To reiterate, They store extra meta data such as: the tagger name, email, and date. Similar to commits and commit messages Annotated tags have a tagging message. Additionally, for security, annotated tags can be signed and verified with GNU Privacy Guard (GPG). Suggested best practices for git tagging is to prefer annotated tags over lightweight so you can have all the associated meta-data.
$ git tag -a v1.4
Executing this command will create a new annotated tag identified with v1.4
. The command will then open up the configured default text editor to prompt for further meta data input.
$ git tag -a v1.4 -m "my version 1.4"
Executing this command is similar to the previous invocation, however, this version of the command is passed the -m
option and a message. This is a convenience method similar to git commit -m
that will immediately create a new tag and forgo opening the local text editor in favor of saving the message passed in with the -m
option.
$ git tag v1.4-lw
Executing this command creates a lightweight tag identified as v1.4-lw.
Lightweight tags are created with the absence of the -a
, -s
, or -m
options. Lightweight tags create a new tag checksum and store it in the .git/
directory of the project's repo.
To list stored tags in a repo execute the following:
$ git tag
To refine the list of tags the -l
option can be passed with a wild card expression:
$ git tag -l *-rc*
v0.10.0-rc1 v0.11.0-rc1 v0.12.0-rc1 v0.13.0-rc1 v0.13.0-rc2 v0.14.0-rc1 v0.9.0-rc1 v15.0.0-rc.1 v15.0.0-rc.2 v15.4.0-rc.3
This previous example uses the -l
option and a wildcard expression of -rc
which returns a list of all tags marked with a -rc
prefix, traditionally used to identify release candidates.
The previous tagging examples have demonstrated operations on implicit commits. By default, git tag
will create a tag on the commit that HEAD
is referencing. Alternatively git tag
can be passed as a ref to a specific commit. This will tag the passed commit instead of defaulting to HEAD.
To gather a list of older commits execute the git log
command.
$ git log --oneline
3b1d230 (HEAD -> master) fixed EOL
460740c reverting back to fourth
0a4a799 restore to third change
f9ec630 Revert to "fourth change"
2e5676f Revert to "fourth change"
c922b14 fourth change
efae9f8 third change
335a181 second change
1976f2d (tag: v1.0) first change
8eca64f empty file
Executing git log
will output a list of commits. In this example we will pick second change
for the new tag. We will need to reference to the commit SHA hash to pass to Git:
$ git tag -a v1.2 335a181
Executing the above git tag
invocation will create a new annotated commit identified as v1.2
for the commit we selected in the previous git log
example.
If you try to create a tag with the same identifier as an existing tag, Git will throw an error like:
fatal: tag 'v0.4' already exists
Additionally if you try to tag an older commit with an existing tag identifier Git will throw the same error.
In the event that you must update an existing tag, the -f FORCE
option must be used.
$ git tag -a -f v1.4 15027957951b64cf874c3557a0f3547bd83b3ff6
Executing the above command will map the 15027957951b64cf874c3557a0f3547bd83b3ff6
commit to the v1.4
tag identifier. It will override any existing content for the v1.4
tag.
Sharing tags is similar to pushing branches. By default, git push
will not push tags. Tags have to be explicitly passed to git push
.
$ git push origin v1.4
Counting objects: 14, done. Delta compression using up to 8 threads. Compressing objects: 100% (12/12), done. Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done. Total 14 (delta 3), reused 0 (delta 0) To [email protected]:atlasbro/gittagdocs.git * [new tag] v1.4 -> v1.4
To push multiple tags simultaneously pass the --tags
option to git push
command. When another user clones or pulls a repo they will receive the new tags.
You can view the state of a repo at a tag by using the git checkout command.
$ git checkout v1.4
The above command will checkout the v1.4
tag. This puts the repo in a detached HEAD
state. This means any changes made will not update the tag. They will create a new detached commit. This new detached commit will not be part of any branch and will only be reachable directly by the commits SHA hash. Therefore it is a best practice to create a new branch anytime you're making changes in a detached HEAD
state.
Deleting tags is a straightforward operation. Passing the -d
option and a tag identifier to git tag
will delete the identified tag.
$ git tag
v1 v2 v3 $ git tag -d v1 $ git tag v2 v3
In this example git tag
is executed to display a list of tags showing v1, v2, v3, Then git tag -d v1
is executed which deletes the v1 tag.
Sometimes it may be a good idea to exclude files from being tracked with Git. This is typically done in a special file named .gitignore
. You can find helpful templates for .gitignore
files at https://github.com/github/gitignore.
Git ignore rules are usually defined in a .gitignore
file at the root of your repository. However, you can choose to define multiple .gitignore
files in different directories in your repository. Each pattern in a particular .gitignore
file is tested relative to the directory containing that file. However the convention, and simplest approach, is to define a single .gitignore
file in the root. As your .gitignore
file is checked in, it is versioned like any other file in your repository and shared with your teammates when you push. Typically you should only include patterns in .gitignore
that will benefit other users of the repository.
.gitignore
uses globbing patterns to match against file names. You can construct your patterns using various symbols:
Pattern | Example matches | *Explanation |
---|---|---|
**/logs |
logs/debug.log logs/monday/foo.bar build/logs/debug.log |
You can prepend a pattern with a double asterisk to match directories anywhere in the repository. |
**/logs/debug.log |
logs/debug.log build/logs/debug.log but not logs/build/debug.log |
You can also use a double asterisk to match files based on their name and the name of their parent directory. |
*.log |
debug.log foo.log .log logs/debug.log |
An asterisk is a wildcard that matches zero or more characters. |
*.log !important.log |
debug.log but not logs/debug.log |
Prepending an exclamation mark to a pattern negates it. If a file matches a pattern, but also matches a negating pattern defined later in the file, it will not be ignored. |
/debug.log |
debug.log but not logs/debug.log |
Patterns defined after a negating pattern will re-ignore any previously negated files. |
debug.log |
debug.log logs/debug.log |
Prepending a slash matches files only in the repository root. |
debug?.log |
debug0.log debugg.log but not debug10.log |
A question mark matches exactly one character. |
debug[0-9].log |
debug0.log debug1.log but not debug10.log |
Square brackets can also be used to match a single character from a specified range. |
debug[01].log |
debug0.log debug1.log but not debug2.log debug01.log |
Square brackets match a single character form the specified set. |
debug[!01].log |
debug2.log but not debug0.log debug1.log debug01.log |
An exclamation mark can be used to match any character except one from the specified set. |
debug[a-z].log |
debuga.log debugb.log but not debug1.log |
Ranges can be numeric or alphabetic. |
logs |
logs logs/debug.log logs/latest/foo.bar build/logs build/logs/debug.log |
If you don't append a slash, the pattern will match both files and the contents of directories with that name. In the example matches on the left, both directories and files named logs are ignored |
logs/ |
logs/debug.log logs/latest/foo.bar build/logs/foo.bar build/logs/latest/debug.log |
Appending a slash indicates the pattern is a directory. The entire contents of any directory in the repository matching that name – including all of its files and subdirectories – will be ignored |
logs/ !logs/important.log |
logs/debug.log logs/important.log |
Wait a minute! Shouldn't logs/important.log be negated in the example on the leftNope! Due to a performance-related quirk in Git, you can not negate a file that is ignored due to a pattern matching a directory |
logs/**/debug.log |
logs/debug.log logs/monday/debug.log logs/monday/pm/debug.log |
A double asterisk matches zero or more directories. |
logs/*day/debug.log |
logs/monday/debug.log logs/tuesday/debug.log but not logs/latest/debug.log |
Wildcards can be used in directory names as well. |
logs/debug.log |
logs/debug.log but not debug.log build/logs/debug.log |
Patterns specifying a file in a particular directory are relative to the repository root. (You can prepend a slash if you |
*these explanations assume your .gitignore file is in the top level directory of your repository, as is the convention. If your repository has multiple .gitignore files, simply mentally replace "repository root" with "directory containing the .gitignore file" (and consider unifying them, for the sanity of your team).
In addition to these characters, you can use #
to include comments in your .gitignore
file:
# ignore all logs
*.log
You can use \
to escape .gitignore
pattern characters if you have files or directories containing them:
# ignore the file literally named foo[01].txt
foo\[01\].txt
You can also define personal ignore patterns for a particular repository in a special file at .git/info/exclude
. These are not versioned, and not distributed with your repository, so it's an appropriate place to include patterns that will likely only benefit you. For example if you have a custom logging setup, or special development tools that produce files in your repository's working directory, you could consider adding them to .git/info/exclude
to prevent them from being accidentally committed to your repository.
In addition, you can define global Git ignore patterns for all repositories on your local system by setting the Git core.excludesFile
property. You'll have to create this file yourself. If you're unsure where to put your global .gitignore
file, your home directory isn't a bad choice (and makes it easy to find later). Once you've created the file, you'll need to configure its location with git config
:
You don’t have to name it .gitignore_global
. You could name it .gitignore
as well. I appended _global
so future me will remember what the file is used for.
$ touch ~/.gitignore_global
$ git config --global core.excludesFile ~/.gitignore_global
For Windows, navigate to the root folder of your user profile usually at C:\Users\[myusername]\
and create a .gitignore_global
file. Then run,
git config --global core.excludesfile "%USERPROFILE%\.gitignore_global"
If using Windows PowerShell then run,
git config --global core.excludesfile "$Env:USERPROFILE\.gitignore_global"
To verify the global exclude file config, run:
git config --global core.excludesfile
The output should be the full path to your file.
You should be careful what patterns you choose to globally ignore, as different file types are relevant for different projects. Special operating system files (e.g. .DS_Store
and thumbs.db
) or temporary files created by some developer tools are typical candidates for ignoring globally.
My current .gitignore_global
file in macOS is below:
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
My current .gitignore_global
file in macOS is below:
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
If you want to ignore a file that you've committed in the past, you'll need to delete the file from your repository and then add a .gitignore
rule for it. Using the --cached
option with git rm
means that the file will be deleted from your repository, but will remain in your working directory as an ignored file.
$ echo debug.log >> .gitignore
$ git rm --cached debug.log
rm 'debug.log'
$ git commit -m "Start ignoring debug.log"
You can omit the --cached
option if you want to delete the file from both the repository and your local file system.
It is possible to force an ignored file to be committed to the repository using the -f
(or --force
) option with git add
:
$ cat .gitignore
*.log
$ git add -f debug.log
$ git commit -m "Force adding debug.log"
You might consider doing this if you have a general pattern (like *.log
) defined, but you want to commit a specific file. However a better solution is to define an exception to the general rule:
$ echo !debug.log >> .gitignore
$ cat .gitignore
*.log
!debug.log
$ git add debug.log
$ git commit -m "Adding debug.log"
This approach is more obvious, and less confusing, for your teammates.
If you have complicated .gitignore
patterns, or patterns spread over multiple .gitignore
files, it can be difficult to track down why a particular file is being ignored. You can use the git check-ignore
command with the -v
(or --verbose
) option to determine which pattern is causing a particular file to be ignored:
$ git check-ignore -v debug.log
.gitignore:3:*.log debug.log
The output shows:
<file containing the pattern> : <line number of the pattern> : <pattern> <file name>
You can pass multiple file names to git check-ignore
if you like, and the names themselves don't even have to correspond to files that exist in your repository.
Git clean can be considered complementary to other commands like git reset and git checkout. Whereas these other commands operate on files previously added to the Git tracking index, the git clean command operates on untracked files. Untracked files are files that have been created within your repo's working directory but have not yet been added to the repository's tracking index using the git add command.
-n
The -n
option will perform a “dry run” of git clean. This will show you which files are going to be removed without actually removing them. It is a best practice to always first perform a dry run of git clean.
-f or --force
The force option initiates the actual deletion of untracked files from the current directory. Force is required unless the clean.requireForce
configuration option is set to false. This will not remove untracked folders or files specified by .gitignore
.
-d include directories
The -d option tells git clean that you also want to remove any untracked directories, by default it will ignore directories.
-x force removal of ignored files
A common software release pattern is to have a build or distribution directory that is not committed to the repositories tracking index. The build directory will contain ephemeral build artifacts that are generated from the committed source code. This build directory is usually added to the repositories .gitignore
file. It can be convenient to also clean this directory with other untracked files. The -x
option tells git clean to also include any ignored files. As with previous git clean invocations, it is a best practice to execute a 'dry run' first, before the final deletion. The -x
option will act on all ignored files, not just project build specific ones. This could be unintended things like .idea
IDE configuration files.
git clean -xf
Like the -d
option -x
can be passed and composed with other options. This example demonstrates a combination with -f
that will remove untracked files from the current directory as well as any files that Git usually ignores.
-i interactive mode
git clean has an "interactive" mode that you can initiate by passing the -i option. The interactive mode will display a What now> prompt that requests a command to apply to the untracked files. The commands themselves are fairly self explanatory.
The git revert
command can be considered an 'undo' type command, however, it is not a traditional undo operation. Instead of removing the commit from the project history, it figures out how to invert the changes introduced by the commit and appends a new commit with the resulting inverse content. This prevents Git from losing history, which is important for the integrity of your revision history and for reliable collaboration.
Reverting should be used when you want to apply the inverse of a commit from your project history. This can be useful, for example, if you’re tracking down a bug and find that it was introduced by a single commit. Instead of manually going in, fixing it, and committing a new snapshot, you can use git revert
to automatically do all of this for you.
Below are the steps to revert a commit:
# Step 1: first check the commit history
git log --oneline
# Step 2: select the commit you want to revert
git revert nd7hjd9
# Step 3: Resolve any conflicts that might arive
# Edit the file(s) in your preferred editor to resolve conflicts
# Then mark the resolved files
git add [file]
# in case of all files
git add .
# Step 4: Complete the revert commit
git commit -m "Revert commit h7i8j9k to fix bug in feature Y"
# Step 5: Push the revert commit to the remote repository
git push origin master
The git reset
command is used to reset current HEAD to a specified state. It can modify the index and working directory depending on the options used. Here are the main types of reset:
-
Soft Reset (
git reset --soft <commit>
):This moves the HEAD pointer to a specified commit but leaves your working directory and staging area (index) unchanged. It's useful if you want to undo a commit but keep your changes staged.
$ git reset --soft HEAD~1
This undoes the last commit, keeping the changes staged.
-
Mixed Reset (
git reset --mixed <commit>
):This is the default mode. It moves the HEAD pointer and updates the index to match the specified commit, but it does not change the working directory. This unstages changes that were committed.
$ git reset HEAD~1
This undoes the last commit and unstages the changes, allowing you to modify them before committing again.
-
Hard Reset (
git reset --hard <commit>
):This resets the HEAD pointer, the index, and the working directory to the specified commit. It will discard all changes, so use this with caution.
$ git reset --hard HEAD~1
This completely removes the last commit and all changes associated with it.
-
Resetting to a Specific Commit:
You can also reset to any specific commit by using its SHA.
$ git reset --hard abc1234
This resets the repository to the state of the commit with SHA
abc1234
.
Remember to always be careful when using --hard
as it irreversibly deletes changes.
The git rm
command can be used to remove individual files or a collection of files. The primary function of git rm
is to remove tracked files from the Git index. Additionally, git rm
can be used to remove files from both the staging index and the working directory. There is no option to remove a file from only the working directory. The files being operated on must be identical to the files in the current HEAD
. If there is a discrepancy between the HEAD
version of a file and the staging index or working tree version, Git will block the removal. This block is a safety mechanism to prevent removal of in-progress changes.
<file>…
Specifies the target files to remove. The option value can be an individual file, a space delimited list of files file1 file2 file3
, or a wildcard file glob (~./directory/*)
.
-f
--force
The -f
option is used to override the safety check that Git makes to ensure that the files in HEAD
match the current content in the staging index and working directory.
-n
--dry-run
The "dry run" option is a safeguard that will execute the git rm
command but not actually delete the files. Instead it will output which files it would have removed.
-r
The -r
option is shorthand for 'recursive'. When operating in recursive mode git rm
will remove a target directory and all the contents of that directory.
--
The separator option is used to explicitly distinguish between a list of file names and the arguments being passed to git rm
. This is useful if some of the file names have syntax that might be mistaken for other options.
--cached
The cached option specifies that the removal should happen only on the staging index. Working directory files will be left alone.
--ignore-unmatch
This causes the command to exit with a 0 sigterm status even if no files matched. This is a Unix level status code. The code 0 indicates a successful invocation of the command. The --ignore-unmatch
option can be helpful when using git rm
as part of a greater shell script that needs to fail gracefully.
-q
--quiet
The quiet option hides the output of the git rm
command. The command normally outputs one line for each file removed.
Executing git rm
is not a permanent update. The command will update the staging index and the working directory. These changes will not be persisted until a new commit is created and the changes are added to the commit history. This means that the changes here can be "undone" using common Git commands.
git reset HEAD
A reset will revert the current staging index and working directory back to the HEAD
commit. This will undo a git rm
.
git checkout .
A checkout will have the same effect and restore the latest version of a file from HEAD
.
In the event that git rm
was executed and a new commit was created which persist the removal, git reflog
can be used to find a ref that is before the git rm
execution.
A Git repository will recognize when a regular shell rm
command has been executed on a file it is tracking. It will update the working directory to reflect the removal. It will not update the staging index with the removal. An additional git add
command will have to be executed on the removed file paths to add the changes to the staging index. The git rm
command acts a shortcut in that it will update the working directory and the staging index with the removal.
As stated above in "Why use git rm
instead of rm
" , git rm
is actually a convenience command that combines the standard shell rm
and git add
to remove a file from the working directory and promote that removal to the staging index. A repository can get into a cumbersome state in the event that several files have been removed using only the standard shell rm
command.
If intentions are to record all the explicitly removed files as part of the next commit, git commit -a
will add all the removal events to the staging index in preparation of the next commit.
If however, intentions are to persistently remove the files that were removed with the shell rm
, use the following command:
git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
This command will generate a list of the removed files from the working directory and pipe that list to git rm --cached
which will update the staging index.
Branches are an important part of working with Git. Any commits you make will be made on the branch you're currently "checked out" to. Use git status
to see which branch that is.
-
Create new branch
$ git branch [branch-name]
-
Switch to the specified branch and updates the working directory
$ git checkout [branch-name]
-
Deletes the specified branch
$ git branch -d [branch-name]
-
Combine the specified branch’s history into the current branch. This is usually done in pull requests, but is an important Git operation.
$ git merge [branch]
Synchronize your local repository with the remote repository on GitHub.com
-
Download all history from the remote tracking branches
$ git fetch
-
Combine remote tracking branch into current local branch
$ git merge
-
Upload all local branch commits to GitHub
$ git push
-
Updates your current local working branch with all new commits from the corresponding remote branch on GitHub.
git pull
is a combination ofgit fetch
andgit merge
$ git pull