Git has three main states that your files can reside in: modified, staged, and committed:
- Modified
- you have changed the file but have not committed it to your database yet.
- Staged
- you have marked a modified file in its current version to go into your next commit snapshot.
- Committed
- the data is safely stored in your local database.
This leads us to the three main sections of a Git project: the working tree, the staging area, and the Git directory.
Working Staging .git directory
Directory Area (Repository)
<----------------- checkout the project-----------------------
Stage Fixes --------------------->
Commit ------------------------>
- The working tree
- is a single checkout of one version of the project. These files are pulled out of the compressed database in the Git directory and placed on disk for you to use or modify.
- The staging area
- is a file, generally contained in your Git directory, that stores information about what will go into your next commit. Its technical name in Git parlance is the “index”.
- The Git directory
- is where Git stores the metadata and object database for your project. This is the most important part of Git, and it is what is copied when you clone a repository from another computer.
- Tracked files
- are files that were in the last snapshot, as well as any newly staged files; they can be unmodified, modified, or staged. In short, tracked files are files that Git knows about.
- Untracked files
- are everything else — any files in your working directory that were not in your last snapshot and are not in your staging area.
The lifecycle of the status of your files:
Untracked ¦-------------------- Tracked ----------------------¦
Unmodified Modified Staged
Add the file ---------------------------------------------------->
Edit the file -------->
Stage the file ------>
<---------- Remove the file
<------------------------------------------Commit
Branching means you diverge from the main line of development and continue to do work without messing with that main line.
When you make a commit, Git stores a commit object that contains a pointer to the snapshot of the content you staged. This object also contains the author’s name and email address, the message that you typed, and pointers to the commit or commits that directly came before this commit (its parent or parents): zero parents for the initial commit, one parent for a normal commit, and multiple parents for a commit that results from a merge of two or more branches.
Let’s assume that you have a directory containing three files, and you stage them all and commit. Staging the files computes a checksum for each one, stores that version of the file in the Git repository (Git refers to them as blobs), and adds that checksum to the staging area.
When you create the commit by running git commit
, Git checksums each subdirectory (in this case, just the root project directory) and stores them as a tree object in the Git repository. Git then creates a commit object that has the metadata and a pointer to the root project tree so it can re-create that snapshot when needed.
Git repository now contains five objects: three blobs (each representing the contents of one of the three files), one tree that lists the contents of the directory and specifies which file names are stored as which blobs, and one commit with the pointer to that root tree and all the commit metadata.
5b1d3
+-----------+
| blob size |
| |
| License |
+-----------+
/
/
98ca9 92ec2 / 911e7
+--------------------+ +--------------------+ +-----------+
| commit size | | tree size | | blob size |
| tree 92ec2 | | blob 5b1d3 README | | |
| author Scott | --> | blob 911e7 LICENSE | --> | |
| commiter Scott | | blob cba0a test.rb | | |
| The initial commit | | | | Library |
+--------------------+ +--------------------+ +-----------+
\
\
\ cba0a
+-----------+
| blob size |
| |
| test |
+-----------+
If you make some changes and commit again, the next commit stores a pointer to the commit that came immediately before it.
98ca9 34ac2 f30ab
+--------------------+ +-----------------+ +-----------------+
| commit size | | commit size | | commit size |
| tree 92ec2 | | tree 184ca | | tree 0de24 |
| parent | | parent 98ca9 | | parent 34ac2 |
| author Scott | <-- | author Scott | <-- | author Scott |
| commiter Scott | | commiter Scott | | commiter Scott |
| The initial commit | | Fixed bug #32 | | ass feature #4 |
+--------------------+ +-----------------+ +-----------------+
¦ ¦ ¦
+------------+ +------------+ +------------+
| Snapshot A | | Snapshot B | | Snapshot C |
+------------+ +------------+ +------------+
A branch in Git is simply a lightweight movable pointer to one of these commits. The default branch name in Git is master. As you start making commits, you’re given a master branch that points to the last commit you made. Every time you commit, the master branch pointer moves forward automatically.
New Branch When you create a new branch, this creates a new pointer to the same commit you’re currently on.
Merge Branch When you try to merge one commit with a commit that can be reached by following the first commit’s history, Git simplifies things by moving the pointer forward because there is no divergent work to merge together — this is called a “fast-forward.”
- Remote reference
- are reference (pointers) in your remote repositories, including branches, tags, and so on.
- Remote-tracking branches
- are references to the state of remote branches. They’re local references that you can’t move; Git moves them for you whenever you do any network communication, to make sure they accurately represent the state of the remote repository. Think of them as bookmarks, to remind you where the branches in your remote repositories were the last time you connected to them.
- Tracking Branches
- are local branches that have a direct relationship to a remote branch. If you’re on a tracking branch and type
git pull
, Git automatically knows which server to fetch from and which branch to merge in.
Anything that has merge conflicts and hasn’t been resolved is listed as unmerged when this command is used git status
. Git adds standard conflict-resolution markers to the files that have conflicts, so you can open them manually and resolve those conflicts. Your file contains a section that looks something like this:
<<<<<<< HEAD:index.html
<div id="footer">contact : [email protected]</div>
=======
<div id="footer">
please contact us at [email protected]
</div>
>>>>>>> iss53:index.html
This means the version in HEAD (your master branch, because that was what you had checked out when you ran your merge command) is the top part of that block (everything above the =======), while the version in your other branch looks like everything in the bottom part. In order to resolve the conflict, you have to either choose one side or the other or merge the contents yourself.
Once solved, the resolution has a little of each section, and the <<<<<<<, =======, and >>>>>>> lines have been completely removed. After you’ve resolved each of these sections in each conflicted file, run git add
on each file to mark it as resolved. Staging the file marks it as resolved in Git.
Checking out a local branch from a remote-tracking branch automatically creates what is called a “tracking branch” (and the branch it tracks is called an “upstream branch”). Tracking branches are local branches that have a direct relationship to a remote branch. If you’re on a tracking branch and type git pull
, Git automatically knows which server to fetch from and which branch to merge in.
When you clone a repository, it generally automatically creates a master
branch that tracks origin/master
. However, you can set up other tracking branches if you wish — ones that track branches on other remotes, or don’t track the master
branch. The simple case is the example you just saw, running git checkout -b <branch> <remote>/<branch>
.
experiment
+----+
| C4 |
+----+
/
L
+----+ +----+ +----+ +----+
| C0 |<-- | C1 |<-- | C2 |<-- | C3 |
+----+ +----+ +----+ +----+
master
The easiest way to integrate the branches, is the merge
command. It performs a three-way merge between the two latest branch snapshots (C3
and C4
) and the most recent common ancestor of the two (C2
), creating a new snapshot (and commit).
MERGE
experiment
+----+
|*C4*|
+----+
/ ^
L \
+----+ +----+ +----+ +----+ +----+
| C0 |<-- | C1 |<-- |*C2*|<-- |*C3*|<--| C5 |
+----+ +----+ +----+ +----+ +----+
master
However, there is another way: you can take the patch of the change that was introduced in C4
and reapply it on top of C3
. In Git, this is called rebasing. With the rebase
command, you can take all the changes that were committed on one branch and replay them on a different branch.
For this example, you would switch
the experiment branch, and then rebase
it onto the master branch.
This operation works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn.
Rebasing the change introduced in C4
onto C3
.
+ - -+
|+C4+|
+- - +
experiment
+----+ +----+ +----+ +----+ +-----+
| C0 |<-- | C1 |<-- | C2 |<-- | C3 |<-- | C4' |
+----+ +----+ +----+ +----+ +-----+
master
Fast-forwarding the master branch applying git checkout master ; git merge experiment
command:
experiment
+----+ +----+ +----+ +----+ +-----+
| C0 |<-- | C1 |<-- | C2 |<-- | C3 |<-- | C4' |
+----+ +----+ +----+ +----+ +-----+
master
Now, the snapshot pointed to by C4'
is exactly the same as the one that was pointed to by C5
in the merge example. There is no difference in the end product of the integration, but rebasing makes for a cleaner history. If you examine the log
of a rebased branch, it looks like a linear history: it appears that all the work happened in series, even when it originally happened in parallel.
master
+----+ +----+ +----+ +----+
| C1 |<-- | C2 |<-- | C5 |<-- | C6 |
+----+ +----+ +----+ +----+
^
\
+----+ +----+ +-----+
| C3 |<-- | C4 |<-- | C10 |
+----+ +----+ +-----+
^ server
\
+----+ +-----+
| C8 |<-- | C9 |
+----+ +-----+
client
You want to merge your client-side changes into your mainline for a release, but you want to hold off on the server-side changes until it’s tested further. You can take the changes on client that aren’t on server (C8
and C9
) and replay them on your master branch by using the --onto
option of git rebase
:
git rebase --onto master server client
master client
+----+ +----+ +----+ +----+ +-----+ +-----+
| C1 |<-- | C2 |<-- | C5 |<-- | C6 |<-- | C8' |<-- | C9' |
+----+ +----+ +----+ +----+ +-----+ +-----+
^
\
+----+ +----+ +-----+
| C3 |<-- | C4 |<-- | C10 |
+----+ +----+ +-----+
server
+ - -+ +- - +
|+C8+|<-- |+C9+|
+- - + + - -+
warning
: Do not rebase commits that exist outside your repository and that people may have based work on.
Rebase vs. Merge: You can get the best of both worlds: rebase local changes before pushing to clean up your work, but never rebase anything that you’ve pushed somewhere.
A remote URL is Git’s fancy way of saying ”the place where your code is stored.” That URL could be your repository on GitHub, or another user’s fork, or even on a completely different server.
You can only push to two types of URL addresses:
- An HTTPS URL like
https://github.com/user/repo.git
- An SSH URL, like
[email protected]:user/repo.git
Git associates a remote URL with a name, and your default remote is usually called origin
.
You can use the git remote add
command to match a remote URL with a name. For example, you’d type the following in the command line:
git remote add origin <REMOTE_URL>
This associates the name origin
with the REMOTE_URL
.
You can use the command git remote set-url
to change a remote’s URL.
The git push
command takes two arguments:
A remote name, for example, origin
A branch name, for example, main
For example:
git push REMOTE-NAME BRANCH-NAME
As an example, you usually run git push origin main
to push your local changes to your online repository.
The simplest setup you’re likely to encounter is a private project with one or two other developers. “Private,” in this context, means closed-source — not accessible to the outside world. You and the other developers all have push access to the repository.
That is one of the simplest workflows. You work for a while (generally in a topic branch), and merge that work into your master branch when it’s ready to be integrated. When you want to share that work, you fetch and merge your master from origin/master if it has changed, and finally push to the master branch on the server.
In this next scenario, you’ll look at contributor roles in a larger private group. You’ll learn how to work in an environment where small groups collaborate on features, after which those team-based contributions are integrated by another party.
Let’s say that John and Jessica are working together on one feature (call this “featureA”), while Jessica and a third developer, Josie, are working on a second (say, “featureB”). In this case, the company is using a type of integration-manager workflow where the work of the individual groups is integrated only by certain engineers, and the master branch of the main repo can be updated only by those engineers. In this scenario, all work is done in team-based branches and pulled together by the integrators later.
You’ll probably want to clone the main repository, create a topic branch for the patch or patch series you’re planning to contribute, and do your work there. The sequence looks basically like this:
git clone <url>
cd project
git checkout -b featureA
#... work ...
git commit
#... work ...
git commit
When your branch work is finished and you’re ready to contribute it back to the maintainers, go to the original project page and click the “Fork” button, creating your own writable fork of the project. You then need to add this repository URL as a new remote of your local repository.
git remote add myfork <url>
You then need to push your new work to this repository. In any event, you can push your work with:
git push -u myfork featureA
Once your work has been pushed to your fork of the repository, you need to notify the maintainers of the original project that you have work you’d like them to merge. You can run the git request-pull
command and email the subsequent output to the project maintainer manually.
The git request-pull
command takes the base branch into which you want your topic branch pulled and the Git repository URL you want them to pull from, and produces a summary of all the changes you’re asking to be pulled.
git request-pull origin/master myfork
If you want to submit a second topic of work to the project, don’t continue working on the topic branch you just pushed up — start over from the main repository’s master branch:
git checkout -b featureB origin/master
You create topic branches for each patch series you work on. You generate email versions of each commit series and email them to the developer mailing list:
git checkout -b topicA
#... work ...
git commit
#... work ...
git commit
Now you have two commits that you want to send to the mailing list. You use git format-patch
to generate the mbox-formatted files that you can email to the list:
git format-patch -M origin/master
(prints out the names of the patch files it creates)
You can either paste the file into your email program or send it via a command-line program. We’ll demonstrate how to send a patch via Gmail. First, you need to set up the imap section in your ~~/.gitconfig~ file.You can set each value separately with a series of git config commands, or you can add them manually, but in the end your config file
[imap]
folder = "[Gmail]/Drafts"
host = imaps://imap.gmail.com #if IMAP server doesn’t use SSL use imap://
user = [email protected]
pass = YX8g76G_2^sFbd
port = 993 #if IMAP server doesn’t use SSL, probably aren’t necessary
sslverify = false #if IMAP server doesn’t use SSL, probably aren’t necessary
When that is set up, you can use git imap-send
to place the patch series in the Drafts folder of the specified IMAP server:
cat *.patch |git imap-send
As before, you can set each value separately with a series of git config
commands, or you can add them manually in the sendemail section in your ~~/.gitconfig~ file:
[sendemail]
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = [email protected]
smtpserverport = 587
After this is done, you can use git send-email
to send your patches:
git send-email *.patch
First, your submissions should not contain any whitespace errors. Run git diff --check
.
Next, try to make each commit a logically separate changeset. If some of the changes modify the same file, try to use git add --patch
to partially stage files.
As a general rule, your messages should start with a single line that’s no more than about 50 characters and that describes the changeset concisely, followed by a blank line, followed by a more detailed explanation. Write your commit message in the imperative: “Fix bug” and not “Fixed bug” or “Fixes bug.” Here is a template:
Capitalized, short (50 chars or less) summary
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase will confuse you if you run the
two together.
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.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, followed by a
single space, with blank lines in between, but conventions vary here
- Use a hanging indent
The Git project has well-formatted commit messages — try running git log --no-merges
there to see what a nicely-formatted project-commit history looks like.
Note: Regarding the “summary” line (the 50 in your formula), the Linux kernel documentation has this to say:
For these reasons, the "summary" must be no more than 70-75 characters, and it must describe both what the patch changes, as well as why the patch might be necessary. It is challenging to be both succinct and descriptive, but that is what a well-written summary should do.
One central hub, or repository, can accept code, and everyone synchronizes their work with it. A number of developers are nodes — consumers of that hub — and synchronize with that centralized location.
This means that if two developers clone from the hub and both make changes, the first developer to push their changes back up can do so with no problems. The second developer must merge in the first one’s work before pushing changes up, so as not to overwrite the first developer’s changes.
Because Git allows you to have multiple remote repositories, it’s possible to have a workflow where each developer has write access to their own public repository and read access to everyone else’s. This scenario often includes a canonical repository that represents the “official” project.
- The project maintainer pushes to their public repository.
- A contributor clones that repository and makes changes.
- The contributor pushes to their own public copy.
- The contributor sends the maintainer an email asking them to pull changes.
- The maintainer adds the contributor’s repository as a remote and merges locally.
- The maintainer pushes merged changes to the main repository.
One of the main advantages of this approach is that you can continue to work, and the maintainer of the main repository can pull in your changes at any time.
It’s generally used by huge projects with hundreds of collaborators; one famous example is the Linux kernel.
- Regular developers work on their topic branch and rebase their work on top of master. The master branch is that of the reference repository to which the dictator pushes.
- Lieutenants merge the developers’ topic branches into their master branch.
- The dictator merges the lieutenants’ master branches into the dictator’s master branch.
- Finally, the dictator pushes that master branch to the reference repository so the other developers can rebase on it.
- Fork the project.
- Create a topic branch from master.
- Make some commits to improve the project.
- Push this branch to your GitHub project.
- Open a Pull Request on GitHub.
- Discuss, and optionally continue committing.
- The project owner merges or closes the Pull Request.
- Sync the updated master back to your fork.
- Clone our fork of the project locally.
- Create a descriptive topic branch.
- Make our change to the code.
- Check that the change is good.
- Commit our change to the topic branch.
- Push our new topic branch back up to our GitHub fork.
$ git clone https://github.com/tonychacon/blink # (1)
# Cloning into 'blink'...
$ cd blink
$ git checkout -b slow-blink # (2)
#Switched to a new branch 'slow-blink'
$ sed -i 's/1000/3000/' blink.ino # (3)
$ git diff --word-diff # (4)
# diff --git a/blink.ino b/blink.ino
# index 15b9911..a6cc5a5 100644
# --- a/blink.ino
# +++ b/blink.ino
# @@ -18,7 +18,7 @@ void setup() {
# // the loop routine runs over and over again forever:
# void loop() {
# digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
# [-delay(1000);-]{+delay(3000);+} // wait for a second
# digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
# [-delay(1000);-]{+delay(3000);+} // wait for a second
# }
$ git commit -a -m 'Change delay to 3 seconds' # (5)
# [slow-blink 5ca509d] Change delay to 3 seconds
# 1 file changed, 2 insertions(+), 2 deletions(-)
$ git push origin slow-blink # (6)
# Username for 'https://github.com': tonychacon
# Password for 'https://[email protected]':
# Counting objects: 5, done.
# Delta compression using up to 8 threads.
# Compressing objects: 100% (3/3), done.
# Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done.
# Total 3 (delta 1), reused 0 (delta 0)
# To https://github.com/tonychacon/blink
# * [new branch] slow-blink -> slow-blink
If you forked from https://github.com/progit/progit2.git, you can keep your master branch up-to-date like this:
git checkout master # (1)
git pull https://github.com/progit/progit2.git # (2)
git push origin master # (3)
- If you were on another branch, return to master.
- Fetch changes from https://github.com/progit/progit2.git and merge them into master.
- Push your master branch to origin.
This works, but it is a little tedious having to spell out the fetch URL every time. You can automate this work with a bit of configuration:
git remote add progit https://github.com/progit/progit2.git # (1)
git fetch progit # (2)
git branch --set-upstream-to=progit/master master # (3)
git config --local remote.pushDefault origin # (4)
- Add the source repository and give it a name. Here, I have chosen to call it
progit
. - Get a reference on progit’s branches, in particular
master
. - Set your
master
branch to fetch from theprogit
remote. - Define the default push repository to
origin
.
Once this is done, the workflow becomes much simpler:
git checkout master # (1)
git pull # (2)
git push # (3)
- If you were on another branch, return to
master
. - Fetch changes from
progit
and merge changes intomaster
. - Push your
master
branch toorigin
.
This approach can be useful, but it’s not without downsides. Git will happily do this work for you silently, but it won’t warn you if you make a commit to master
, pull from progit
, then push to origin~ — all of those operations are valid with this setup. So you’ll have to take care never to commit directly to ~master
, since that branch effectively belongs to the upstream repository.
Can be of nearly any format that GitHub recognizes as prose. For example, it could be README
, README.md
, README.asciidoc
, etc. If GitHub sees a README file in your source, it will render it on the landing page of the project.
Many teams use this file to hold all the relevant project information for someone who might be new to the repository or project. This generally includes things like:
- What the project is for
- How to configure and install it
- An example of how to use it or get it running
- The license that the project is offered under
- How to contribute to it
Since GitHub will render this file, you can embed images or links in it for added ease of understanding.
If you have a file named CONTRIBUTING
with any file extension, GitHub will show Opening a Pull Request when a CONTRIBUTING file exists when anyone starts opening a Pull Request.
The idea here is that you can specify specific things you want or don’t want in a Pull Request sent to your project. This way people may actually read the guidelines before opening the Pull Request.
/etc/gitconfig
Contains values applied to every user on the system and all their repositories.- ~~/.gitconfig~ or ~~/.config/git/config~ Values specific personally to you, the user.
config
file in the Git directory (that is,.git/config
) of whatever repository you’re currently using: Specific to that single repository.
Create a file to add a list of patterns to match the files to be ignored.
The rules for the patterns you can put in the .gitignore
file are as follows:
- Blank lines or lines starting with
#
are ignored. - Standard glob patterns work, and will be applied recursively throughout the entire working tree.
- You can start patterns with a forward slash (
/
) to avoid recursivity. - You can end patterns with a forward slash (
/
) to specify a directory. - You can negate a pattern by starting it with an exclamation point (
!
). - You can also use two asterisks to match nested directories;
a/**/z
would matcha/z
,a/b/z
,a/b/c/z
.
In the simple case, a repository might have a single .gitignore
file in its root directory, which applies recursively to the entire repository. However, it is also possible to have additional .gitignore
files in subdirectories. The rules in these nested .gitignore
files apply only to the files under the directory where they are located.
man gitignore
show more details.
# ignore any files ending in “.o” or “.a”
*.[oa]
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
/TODO
# ignore all files in any directory named build
build/
# 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
Is very much like a branch that doesn’t change — it’s just a pointer to a specific commit.
Are stored as full objects in the Git database. They’re checksummed; contain the tagger name, email, and date; have a tagging message; and can be signed and verified with GNU Privacy Guard (GPG). It’s generally recommended that you create annotated tags so you can have all this information; but if you want a temporary tag or for some reason don’t want to keep the other information, lightweight tags are available too.
A remote repository is generally a *bare repository* — a Git repository that has no working directory. In the simplest terms, a bare repository is the contents of project’s .git
directory and nothing else. It’s just the Git data.
- Local Protocol
- The most basic protocol is the Local protocol, in which the remote repository is in another directory on the same host. If you have a shared mounted filesystem, then you can clone, push to, and pull from a local file-based repository. To clone a repository like this, or to add one as a remote to an existing project, use the path to the repository as the URL.
- The HTTP Protocols
-
- Smart HTTP
- Smart HTTP operates very similarly to the SSH or Git protocols but runs over standard HTTPS ports and can use various HTTP authentication mechanisms, meaning it’s often easier on the user than something like SSH, since you can use things like username/password authentication rather than having to set up SSH keys.
- Dumb HTTP
- The Dumb protocol expects the bare Git repository to be served like normal files from the web server. The beauty of Dumb HTTP is the simplicity of setting it up. Basically, all you have to do is put a bare Git repository under your HTTP document root and set up a specific post-update hook, and you’re done.
- The SSH Protocol
- A common transport protocol for Git when self-hosting is over SSH. This is because SSH access to servers is already set up in most places — and if it isn’t, it’s easy to do.
- The Git Protocol
- This is a special daemon that comes packaged with Git; it listens on a dedicated port (9418) that provides a service similar to the SSH protocol, but with absolutely no authentication. In order for a repository to be served over the Git protocol, you must create a
git-daemon-export-ok
file — the daemon won’t serve a repository without that file in it — but, other than that, there is no security.
In order to initially set up any Git server, you have to export an existing repository into a new bare repository — a repository that doesn’t contain a working directory. Now that you have a bare copy of your repository, all you need to do is put it on a server and set up your protocols.
To clone a local repository. Git tries to use hardlinks or directly copy the files it needs.
To clone a local repository. Git fires up the processes that it normally uses to transfer data over a network, which is generally much less efficient (clean copy of the repository with extraneous references or objects left out).
To add a local repository to an existing Git project. (you can push to and pull from that remote via your new remote name local_proj as though you were doing so over a network)
The Dumb protocol expects the bare Git repository to be served like normal files from the web server. The beauty of Dumb HTTP is the simplicity of setting it up. Basically, all you have to do is put a bare Git repository under your HTTP document root and set up a specific post-update hook, and you’re done.
cd /var/www/htdocs/
git clone --bare /path/to/git_project gitproject.git
cd gitproject.git
mv hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update
# In this particular case, we’re using the /var/www/htdocs path that is
# common for Apache setups, but you can use any static web server — just
# put the bare repository in its path.
The post-update
hook that comes with Git by default runs the appropriate command (git update-server-info
) to make HTTP fetching and cloning work properly.
git clone https://example.com/gitproject.git
To clone repository.
To clone a Git repository over SSH. (if don’t specify the optional username, Git assumes the user is currently logged in as)
To clone a Git repository with shorter scp-like syntax for the SSH protocol. (if don’t specify the optional username, Git assumes the user is currently logged in as)
Clone your repository to create a new bare repository. With this you have a copy of the Git directory data in your my_project.git directory.
( cp -Rf my_project/.git my_project.git
equivalent command* )
scp -r my_project.git [email protected]:/srv/git
Set up new repository by copying bare repository over. Assuming that //srv/git/ exists on the server git.example.com .
git clone [email protected]:/srv/git/my_project.git
Clone repository by users who have SSH-based read access to the //srv/git/ directory on that server. ( if a user SSHs into a server and has write access to the //srv/git/my_project.git/ directory, they will also automatically have push access)
Will automatically add group write permissions to a repository. (this command, you will not destroy any commits, refs, etc. in the process)
ssh [email protected]
cd /srv/git/my_project.git
git init --bare --shared
Set up Git daemon serving repositories using the “Git” protocol. (If you’re running a firewall, you’ll also need to punch a hole in it at port 9418 on the box you’re setting this up on) [ –reuseaddr allows the server to restart without waiting for old connections to time out –base-path allows people to clone projects without specifying the entire path //srv/git// at the end tells the Git daemon where to look for repositories to export ]
# Place a file in /etc/systemd/system/git-daemon.service
[Unit]
Description=Start Git Daemon
[Service]
ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/srv/git/ /srv/git/
Restart=always
RestartSec=500ms
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=git-daemon
User=git
Group=git
[Install]
WantedBy=multi-user.target
# run systemctl enable git-daemon to automatically start the service on
# boot, and can start and stop the service with, respectively, systemctl
# start git-daemon and systemctl stop git-daemon.
# You to have to tell Git which repositories to allow unauthenticated
# Git server-based access to. You can do this in each repository by
# creating a file named git-daemon-export-ok.
cd /path/to/project.git
touch git-daemon-export-ok
# The presence of that file tells Git that it’s OK to serve this project
# without authentication.
Specifier | Description of Output |
---|---|
%H | Commit hash |
%h | Abbreviated commit hash |
%T | Tree hash |
%t | Abbreviated tree hash |
%P | Parent hashes |
%p | Abbreviated parent hashes |
%an | Author name |
%ae | Author email |
%ad | Author date(format respect the –date=option) |
%ar | Author date, relative |
%cn | Committer name |
%ce | Committer email |
%cd | Committer date |
%cr | Committer date, relative |
%s | Subject |
Option | Description |
-<n> | Show only the last n commits |
–since, –after | Limit commits after the specified date |
–until, –before | Limit commits before the specified date |
–author | Only show commits in which the author |
entry matches the specified string | |
–committer | Only show commits in which the committer |
entry matches the specified string | |
–grep | Only s commits with commit msg containing str |
-S | Only s commits adding or rm code matching str |