Last updated 11 December 2017.


This document is intended to outline a basic process of using Git in the context of a basic site building, testing and deployment process. While there are many possible approaches to fitting Git into this process, this particular set of procedures should work in most circumstances and contains many best practices for using Git in this manner. When applied properly and with some forethought, Git is a very powerful tool for helping to manage collaboration, configuration and code changes during the life cycle of a Drupal-based project. Further documentation will be written to show how best to integrate other tools such as Drush into this process.

This documentation assumes that the project will be following a basic 4-tier development environment model: developers work on most code locally, then push that code up through Development, Staging and Production environments. It can easily be adapted to fewer tiers, as necessary. We will be illustrating the process by building out a Drupal site called ‘FooProject’, and will use fooproject as a placeholder anywhere a project or site name would be used.

What can you manage with code?

One challenge of using any kind of change management with Drupal is that many configuration changes normally reside only in the project’s database. You can move some of these configuration settings into code using certain Drupal modules, such as Ctools exportables, the Features module or the Configuration Management module. You may want to take advantage of these tools to export as much of your site’s configuration as possible into your repository for deployment and collaboration purposes. Additionally, the Backup and Migrate module can save database exports as files within your Drupal file tree; these are managed with Git like any other file.

Fortunately, since a Drupal site's module and theme files already live in code, you can manage those in Git without any extra tools.

Creating the Central Repository

When working on a project with multiple environments, a good first step after server provisioning is to create a central repository from which all other environments will pull. This could live on one of the servers provisioned for the project, a Gitosis server, a Gitolite server, a GitLab server, Github or any other repository hosting solution. For purposes of illustration, assume that your FooProject is running on a server configured with a user named ‘fooproject’ and all three environments will be running from separate directories under that user’s public_html directory. For convenience, create the central repository inside fooproject’s home directory.

(On your remote server)

$ cd ~
$ git init --bare fooproject.git

This command creates a new directory called fooproject.git that contains all of the git objects. This directory is not the working tree, where you edit and commit code. Rather, it is simply the central location for the git objects and history, and is essentially empty at this point.

Locally Cloning Drupal

This example begins the development process on the developer’s local development environment, but you could follow these steps on the server as well. Let's clone Drupal (version 7) to create a local development environment.

(On your local development environment)

$ git clone fooproject
$ cd fooproject
$ git checkout 7.x 

The first command clones the Drupal core Git repository from and saves it in a directory named fooproject. The fooproject directory will become your working tree. The final command, git checkout 7.0, ensures your code is on the Drupal 7.0 release. When using Drupal 8, note the addition of a decimal place in the version. The equivalent command is git checkout 8.0.0 To choose another release before install, you can run the following command to view a list of all releases:

$ git tag

Then, to switch to the version you want, you would type the following command, where <tagname> is the name of the release you want to use:

$ git checkout <tagname>

Updating Remotes

You’ll need to update your remotes to reflect that you won’t be pushing to with your project’s code. You should rename the original origin remote (the Drupal project repository) to ‘drupal’ and create a new origin pointed at the bare repository you’ve created on your server.

(On your local development environment)

$ git remote rename origin drupal
$ git remote add origin path/to/your/central/git/repo

(example: ssh://

To see a list of your remote repositories, run the command:

$ git remote

For a more detailed listing that includes the remote repositories' URLs, add a -v flag (for verbose) to the end of the command:

$ git remote -v

Creating a Working Branch

Now, you need a branch where you can track not only Drupal core, but also all of the contributed and custom modules and themes for your site. Create a branch using the command:

(On your local development environment)

$ git checkout -b fooproject

This command creates a new branch named fooproject and checks it out. It is equivalent to running the commands:

$ git branch fooproject
$ git checkout fooproject

You can use this fooproject branch as a working branch to add contributed and custom modules and themes to your site. Consider it the equivalent of the default Git ‘master’ branch for your project. For more information on standard Git development and branching see a post on A successful Git branching model.

At this point, you should complete the Drupal installation process to get a working local installation. For Drupal 8, you'll need to do 'composer install' in the Drupal site root before running the web installer:

$ composer install

Setting up the .gitignore file

There are a few things that you probably do not want to have tracked in the repository, namely sites/default/files and sites/default/settings.php. You would exclude the files directory if you prefer only to track application files in Git and to omit site content from the repository. You probably want to exclude settings.php because it contains sensitive database access information and will be different on each environment.

One way to tell Git to exclude certain files and directories from the repository is to set up a .gitignore file. When you clone Drupal 7 from the Git repository, it comes with a .gitignore file. Drupal 6 does not come with a .gitignore file at the time of this writing.

Drupal 8 comes without a .gitignore but has an example.gitignore from which you can create a .gitignore. If you wish to transfer configuration between sites using git, you probably need to change the location of the config directory (which is set in settings.php) to one which is tracked by git. By default, the config directory is in sites/default/files, and by default the .gitignore which you create from example.gitignore will exclude this directory path.

The settings in the Drupal 7 default .gitignore file are as follows:

# Ignore configuration files that may contain sensitive information.

# Ignore paths that contain user-generated content.

Customizing .gitignore

You may want to keep the above settings for your own site. However, if you decide to use different version control policies for your site, for example by deliberately excluding certain modules, themes, or libraries from your repository, you need different .gitignore settings. Here are some options for getting around the default .gitignore settings:

  1. If you don't want sites/all to be controlled at all (you want to ignore all modules and themes and libraries), add a file at sites/all/.gitignore with the contents a single line containing nothing but *.
  2. Simply change the .gitignore and commit the change. You won't be pushing it up to 7.x right?
  3. If you track core code using downloads (and not git) you can simply change the .gitignore and check it into your own VCS.
  4. Add extra things into .git/info/exclude. This basically works like .gitignore (it has good examples in it) and is not under source control at all.
  5. Add an additional master gitignore capability with git config core.excludesfile .gitignore.custom and then put additional exclusions in the .gitignore.custom file.

Note that only 1 and 2 are completely source-controlled. In other words, #3, 4, and 5 would have a slight bit of configuration on a deployment site to work correctly, but they work perfectly for a random dev site.

For more information about the above options, see Randy Fay's blog post:

If you add a new ignore file or edit one that is not being tracked by Git, remember to add it to your Git repository using the git add command, and then commit those changes using git commit. For example:

$ git add .gitignore.custom
$ git commit -m "Initial FooProject commit"

Creating a global .gitignore file

You can also create global ignore settings across all of your Git projects. First, you create a global .gitignore file in your home directory (~/.gitignore). Then, you add it to your global configuration using the following command:

git config --global core.excludesfile ~/.gitignore

Pushing Code to the Central Repository and Completing Initial Deployment

Now, you can push your code up to the origin remote on your server:

(On your local development environment)

$ git push origin fooproject

This command copies your local branch fooproject to a branch of the same name in your remote repository origin.

You can now provision your other tiers with this code from the repository. Log into your server and provision a development environment from the code you’ve committed:

(On your remote server)

$ git clone --branch fooproject ssh:// fooproject_dev

Now, you have a fooproject_dev directory that you can use as the root of a new virtual host. You can proceed through the normal Drupal installation process using this development copy of your site and a separate database for it. Repeat this process for the Staging and Production environments- we'll assume that they live on the same server in directories fooproject_stg and fooproject_prod.

In Drupal 8 the site should be installed only once (rather than repeating the install for dev, staging and production sites), and the different versions of the site should be created with databases set up using a dump of the database from the first copy of the site. This is because each install creates a distinct site with a site UUID, which would prevent import of configuration between the dev, staging and production versions of the site (subject to #1613424: Allow a site to be installed from existing configuration being solved). After copying a site's database, it will be necessary to clear the caches (which can be done from Manage > Configuration > Performance, or using drush from command line).

Because the install is only run on the first copy of the site, it will also be necessary to open settings.php on each version of the site, and manually adjust the database settings in it to use the relevant copy of the database for each version of the site. Naming the databases similarly to the code directories, such as fooproject_dev, fooproject_stg, fooproject_prod, can help avoid confusion .

If you have set up a default .gitignore which excludes the sites/*/default folder before copying the code to each version of the site using git, settings.php and the config directory (assuming it is still in the default location) will not have been copied when copying the code with git. In that case, so you would need to manually copy settings.php to each version of the site, and you would need to either manually copy the config directory or rebuild it, in order to get the copies of the site working. For troubleshooting after copying a site see

Adding Contributed Modules and Themes

The site development process rarely ends with core Drupal - you'll likely be adding contributed modules and themes throughout the development process. There are a number of possible approaches to this process that additional documentation on Git Submodules, Drush and Dog will describe. For the purposes of this documentation, we are not concerned with keeping Git history for contributed modules. Simply download and install these modules and themes to your site and add them to your main development branch. Let’s use Views as an example:

$ cd sites/all/modules
$ wget
$ tar -xzf views-7.x-3.0-beta.tar.gz
$ rm views-7.x-3.0-beta.tar.gz

If you check the status of your repository at this point, Git will point out to you that you have some new untracked files living in your working tree:

$ git status

# On branch fooproject
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#	views/
nothing added to commit but untracked files present (use "git add" to track)

So, we’ll now add them to the Git index:

$ git add views
$ git commit -m “Added Views 3.0-beta”

You can now push this code back up to your central repository and pull it down to your Dev server:

(On your local development environment)

$ git push origin fooproject

(On your remote server while inside the fooproject_dev directory)

$ git pull origin fooproject

At this point, you should see the Views code in both your local environment as well as on the Development server’s codebase, and you should be able to enable this module in both environments. You can follow this basic procedure for adding any contributed module, contributed theme, or custom code to your site.

Topic / Issue Branches

As your project progresses, you may find yourself in a situation where you need to work on an issue or feature outside of the main code-base, where your code changes won’t impact your fellow developers or your client. Alternately, it’s not uncommon to engage in fixing an issue with your code, only to find that the solution is more involved than you have resources to commit to it at the moment. In these situations. it’s helpful to be comfortable with Git’s branching system in order to give you a clean sandbox to work on your changes without losing the ability to pull code from the central repository.

Assuming you're currently on the main fooproject branch before you begin working on an issue submitted by your client, the workflow is simple:

$ git checkout -b issue_606_theme 

This command creates a new branch for the issue based on the branch you were on before issuing the checkout command. At this point, you can work on code and commit changes to this local branch without any worry of losing your work in progress or being able to switch back to the main code-base to work on other issues.

Let’s assume that this particular issue involved changing something in your theme’s page.tpl.php file. Once you've edited the file, you can follow the basic change/add/commit workflow of Git:

$ git add sites/all/themes/footheme/page.tpl.php
$ git commit -m “Issue 606: Removed offending div from page.tpl.php”

You can make as many changes and commits to your branch as required to fix your issue or implement your feature. At this point, all of these commits are living in a branch that exists only on your local copy of the repository. If you have need to switch back to the main branch, you can add and commit your files in your topic branch and issue the following command to get back:

$ git checkout fooproject

Once you've brought your topic branches back into your mainline fooproject branch, as detailed below, it’s a good idea to clean up after yourself. When you've decided you no longer need that topic branch, deleting it is a simple command:

$ git branch -d issue_606_theme

If you’ve created a topic branch but never merged it back into it’s parent, Git will keep you from deleting it accidentally. If you’re sure that you no longer wish to have this unmerged branch, simply replace -d with -D to force deletion of the unmerged branch.

Bringing Branches Back into the Main Codebase

Once you have finished working on your fix or feature, you’ll need to bring those changes from your topic branch back into the main fooproject branch. There are a number of different strategies and opinions on managing the merging of code using either Git’s rebase or merge commands. Here are some basic considerations:

  1. If your branch’s commit history is considered public (i.e. multiple developers working on the same topic branch in their own repositories) - ALWAYS choose Merge.
  2. If you’re concerned with keeping a clean, linear history - Rebase.

Please note that these ground rules are nearly mutually exclusive.

For the purposes of this documentation, let's assume that you are going to use the basic merge-based strategy. For suggestions about using Git's rebase command, read Randy Fay's post at


If you are the primary developer on a project, and you are not concerned with maintaining a completely linear history from your topic branches, using Git’s merge command provides a straightforward way for you to bring your topic branches back into your mainline codebase.

$ git checkout fooproject # Puts you back into your main branch
$ git pull # Fetch and merge changes from your main repo back into your local fooproject, if any exist
$ git merge issue_606_theme  ## Merges your topic branch back into fooproject
$ git push ## Sends your newly updated fooproject code back to the origin repository

Staging and Production - Tag Based Deployment

You could simply allow your Staging and Production environments run from the code you’ve been adding and merging into your main fooproject branch - but it’s worth considering the fact that the fooproject branch is going to be moving forward as development progresses even after your site’s launch, and production sites should really be running a single, well-tested snapshot of the code-base during ongoing development. For this reason, it’s a good idea to use tags instead of branches for managing your non-development code. Tags are simply references to the state of the code-base at a specific commit - a snapshot of the project at one specific moment in time.

When your code has been tested and is ready to be deployed into the production environment, you could follow this process locally:

$ git tag prod_20110419  ## Creating a tag from the current commit.  You can specify a commit here if you wish.

Now, you can push this tag up to your repository:

$ git push origin prod_20110419 

Now, in your server’s fooproject_prod directory:

$ git pull 
$ git checkout prod_20110419

Handling Hotfixes

An inverse procedure can be used to handle changes that need to be made to code in the production environment. Because your production and staging environments are running from tags, they are considered to have a ‘detached HEAD’ - with no commit history, and no branch to commit into. You probably saw Git warning you of this situation when you checked the tag out. Still, git makes it easy to manage hotfixes in this way. For example, you’ve been told about a bug that was just found in production, and it needs to be cleaned up right away. Open your local development copy and perform the following:

$ git checkout prod_20110419 # switching to the offending production branch
$ git checkout -b prod_hotfix_issue_707 # you’re now starting a new branch that begins at your current prod tag
# Code to fix the problem
$ git add <changed files to be committed>
$ git commit -m “Production hotfix for issue 707: Fixing production bug”

Now, you can create a new tag from this commit to run on

$ git tag prod_20110515_hotfix_707 # create the new tag
$ git push origin prod_20110515_hotfix_707 # and push it to the repository

Now, logging in to the production repository:

$ git fetch --tags #update the dev repo with your pushed changes from production
$ git checkout prod_20110515_hotfix_707 # puts you back into a detached HEAD state against your new tag

Now, you’ll can merge that code back into your local fooproject branch

$ git checkout fooproject
$ git merge prod_20110515_hotfix_707 # brings the hotfix code back into the mainline code-base

Updating Drupal Core

In this workflow, managing updates to Drupal core is a fairly trivial process:

$ git checkout fooproject # making sure we’re on our main fooproject branch
$ git fetch drupal # update our repository with changes from the main Drupal upstream repo
$ git merge 7.1 # merge in the updates to Drupal

Then, run update.php and your code and database should be properly updated. If you wish to test the upgrade first, you could always create a new topic branch for the update testing process and merge the Drupal release tag to that branch before bringing it into your mainline codebase. From there, simply create new stg_ and prod_ tags, push them to the repository and pull them into your other environments as above, making sure to run update.php after checking them out!

Notice that in this particular workflow, modules are kept as part of your own code-base and not pulled with Git from Update these modules as per the normal Drupal documentation or with the drush up command, run update.php, then add and commit the updated modules to your repository.

Managing the Database (& your sites/default/files directory)

Data always needs to flow down-stream. That means your live database should be copied and imported to your test site, your dev site, and your local environment - and never moved the opposite direction. This is to prevent data loss. If you have users creating accounts, posting comments, or modifying content - that should all be happening on your live site. If you copy a development database into production any of those changes will be lost.

Because data should never be moved up-stream, it's a good idea to have at least three copies of your drupal site to work with. The live site, where your data grows, but you never do any development. The development site, that will use a recent copy of the database and files from the live site, but has no active changes so that it's a safe place to work on code, install new modules, and do other things that might be considered "too risky" to do on the live site. And the test site, where you can push up the code you've changed on the development site, and pull down the data that's been growing on the live site. This gives you a chance to see what will happen when your changes are pushed into production. A place to do test deployments, where the code and the data can be safely tested together.

For managing configuration in Drupal 8, please refer to the Managing Configuration in Drupal 8 documentation. In Drupal 7 and earlier versions, the database contains a mix of both data and configuration. In order to safely deploy configuration changes you may have made to your development site, they will need to be stored in code. There are several ways to do this:

If you are unable to get your configuration changes into code, you can also keep very good notes of every single change that was made, and manually make them again both on the test site, and on production.

Using Submodules

Git submodules allow foreign repositories to be embedded within a dedicated subdirectory of the source tree, always pointed to a particular commit. See git help submodule for more information. Submodules are ideal to capture both contributed modules and custom modules in a git-managed Drupal installation.

Moving a Custom Module or Theme into its own repository

Custom modules and themes can be tracked as a git submodule.


In order to install Display Suite as a submodule to the main d8 repository.
First find the Repository of the Drupal module at the "Version control" page of the module.

cd <drupal_root>
git submodule add sites/all/modules/_contrib_repos/ds

After adding the submodule you should have a file next to your .git folder named .gitmodules:

[submodule "sites/all/modules/_contrib_repos/ds"]
	path = sites/all/modules/_contrib_repos/ds
	url =

The submodule will not automatically upgrade with the main Drupal repository, but you can use the normal git commands in the submodule folder:

cd  sites/all/modules/_contrib_repos/ds
git status

Once a developer has added, committed, and pushed the update for the Display Suite submodule to the central repository, everyone else can update their local repositories easily:

cd <drupal_root>
git submodule init # This is only necessary when your are grabbing the submodules for the first time.
git submodule update
git submodule foreach git checkout master # This fix the issue of detached heads for some submodule
workflow.jpg142.19 KB
drupal_git_repositories.png83.92 KB


benoit.pointet’s picture

I suggest the origin gets renamed to drupalorg rather than drupal for the sake of clarity.

bryan kennedy’s picture

If you think this is a good idea, can you submit an issue to the infrastructure issue queue?

LandonAB’s picture

I would really love a github specific howto. Really stumbling through how to do this with myself, github and a remote collaborator. Especially the part about provisioning the server.



logickal’s picture

I'm trying to work up specific documentation for github as well - just taking me some time.

AlanAtLarge’s picture

First off, great job, thanks. It's exactly the info I've been looking for. As a beginner in version control I was feeling lost.

On your post, I'm getting stuck around the same spot. I'm not clear on which "box" things reside and are created on, i.e. the branches like the fooproject_dev are created on the Repo and cloned to a hosting server, or created on the hosting server itself?

In my case what I'm trying to set up would look like:

REPO (Github)
HOST (Somewhere)

I think a basic diagram with boxes and a few arrows would really help me.

Last thing, any tips if the HOST does not have Git, and/or does not allow shell access?

Thanks again

mexicoder’s picture

Hi Alan

see my comments below for a few things which may be helpful- still a work in progress though!

bxtaylor’s picture

Is it advisable to rename the remotes in your development, staging and production environments to point to your repository's new origin in the same way that you do for your local environment:

You’ll need to update your remotes to reflect that you won’t be pushing to with your project’s code. You will want to rename the original origin remote (the Drupal project repository) to ‘drupal’ and create a new origin pointed at your bare repository you’ve created on your server.

$ git remote rename origin drupal
$ git remote add origin path/to/your/central/git/repo

(example: ssh://

logickal’s picture

The way this walkthrough works, your initial (local) repository should be the only repo that you need to rename origin - it's the only one that has pulled from d.o. Your other environments will have your own central repository as origin, because that's where you cloned them from.

bxtaylor’s picture

I probably should have poked around the different environments a little more; I would've found the same answer you just provided.

For instance, in my development environment, I ran git remote -v and it lists where the remotes point to.

Thanks again.

johanneshahn’s picture

command line

$ git remote add origin path/to/your/central/git/repo

was a bit confusing me.
we have set a fix projectname "fooproject" and are current in path /home/...
why not change this line to?

$ git remote add origin home/[user]/fooproject.git

sassafrass’s picture

In a multisite setup using Git, what are the advantages and disadvantages of:

1. Setting up nested repositories for each site? For example, having a Git repository for Drupal core but then also having a repository for each sites/site1, sites/site2....etc.

2. Setting up one repository with separate branches for each site?

3. Setting up one repository using submodules for each site?

4. Setting up one single repository?

Lynn Haas

jozzhart’s picture

I too am interested in the best approach for managing multiple sites from one core.

midmood’s picture

Hi, i followed the instruction to set version control, tried to use as bare repo.
when i go to the final push :

git push origin myproject #where origin is an ssh://path/to/repo

i got this result:

Counting objects: 108330, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (23412/23412), done.
Writing objects: 100% (108330/108330), 30.19 MiB | 53 KiB/s, done.
Total 108330 (delta 80222), reused 106501 (delta 78600)

followed by a list of:

remote: You are not <supposed drupal user name> <supposed drupal user email>.


remote: error: hook declined to update refs/heads/myproject
protocol error: error refs/heads/myproject *** PERMISSION DENIED *** hook declined
To ssh://<username><mydomain>/<myrepo>
 ! [remote failure]  myproject -> myproject(remote failed to report status)
error: failed to push some refs to 'ssh://<username><mydomain>/<myrepo>'


If i try the same using bettercodes service as repo provider. I get this:

Fetching remote heads...
updating 'refs/heads/myproject<'
  from 0000000000000000000000000000000000000000
  to   <hash>

then it takes a lot (minutes) until it output:
the number of object to be transferred (usually more then 100,000)
then it stall hours before outputting an http error

Any suggestion?

Hnatt’s picture

Check if credenials of commits you are trying to push are the same as on gitenterprise. If there are commits done by somebody else, you can't push them under your name.

I had similar problem today, but not with drupal, just with some of my projects hosted on Git Enterprise after I switched to another computer. I viewed git log | head and found out that I commited a change before setting my and to those from gitenterprise. So the username and email was taken from OS user account. Resetting last commit did the trick for me: git reset HEAD~1. After that I set and and commited last change again and than managed to push it to remote repo.

UPD: But looks like your problem is easier. <supposed drupal user name> and <supposed drupal user email> should be those from Git Enterprise. See "Help" tab in your gitent-repo web frontend.

midmood’s picture

supposed drupal user name refers to actual drupal users, working on and patching drupal itself.

i did this:
cloned drupal from drupal org
changed remote: drupal git repo is now drupalorg, and my project on gitenterprise became origin.

If I type git history I can see all the commits of the drupal users working on the project, and I suppose git enterprise is complaining those history items.

I hope i was clear... not so experienced yet.

Tor Arne Thune’s picture

Anchor "updatecore" is not correct.
<h2 code="updatecore"> should be <h2 id="updatecore"> :)

animare’s picture

I get the following error when trying to git push origin:

error: src refspec ....... does not match any
error: failed to push some refs to 'ssh://.......'

Please help,


eljakeo’s picture

Obviously, this was posted a long time ago, but I am getting the exact same error (at the initial push of drupal to the remote) and hoping someone could answer it.

Dr Trichome’s picture

I have been running a drupal site for several years and when switched to git, I thought this would be a good time to put my site under version control. I am very new to git, so I apologize if my questions do not make a lot of sense. I followed the above instructions to clone the repository from

$ cd ~/Sites
$ git clone fooproject
$ cd fooproject
$ git checkout 7.0

I got the usual "detached head" warning:

 Note: checking out '7.0'.
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 4979149... Drupal 7.0

So right here I'm stuck. After reading about "detatched HEAD", it is clear that I need my head attached. Also, my site is running drupal 6, and I do not want to upgrade to 7.0 yet, just apply security and other updates to my 6.x site.

I decided I needed a local branch, so I followed git's suggestion above, and created a branch and checked out the new branch.

git checkout -b 7.0
Now I have my own branch of drupal 7.0.

When I type git branch I see:

* 7.0

where I am on branch 7.0 in my repository, and I have an 8.x branch from where HEAD pointed when I cloned the repository.
This is where I get stuck again. If I try to update my 7.0 branch from using git fetch , won't I get the 8.x updates, because that is where HEAD points in the repository? Is fetch always going to fetch whereever HEAD is in the upstream remote?

Also, I do not quite understand the instructions above on Updating Drupal Core:

$ git checkout fooproject # making sure we’re on our main fooproject branch
$ git fetch drupal # update our repository with changes from the main Drupal upstream repo
$ git merge 7.1 # merge in the updates to Drupal

Since a specific tag (7.0) was checked out/branched, there will never be any updates to that tag. Why would fetch ever be used? Shouldn't the commands be the following:

git checkout fooproject # making sure we’re on our main fooproject branch
git branch 7.1 drupal # get a new local branch of 7.1 from assuming the upstream remote was renamed to drupal.
git merge 7.1 # merge in the updates in the 7.1 release into my fooproject code

Thank you for your patience.

Freso’s picture

You don't have to be "attached", being "detached" simply means that you're not going to get updates etc. that would follow from being on a branch, like a given tag can belong to a variety of branches. Whenever you need to be on a branch, simply checkout the branch you wish to go to.

If you wish to stay on 6.x, you will need to check out one of the 6.x tags (the 6.22 tag should bring you to the latest release) or the 6.x branch.

Even if you're "detached" due to being on a non-changing tag, fetch will still pull in the data from the origin repository (or whichever repos you specify), which also pulls in e.g. new tags, allowing you to do git checkout/merge new-tag. Note here, that fetch doesn't change any of the files you're currently working on, it just fetches data from the external repository you specify.

Dr Trichome’s picture

Wow. I have a lot more to learn. So, if I checkout the 6.x branch, and then fetch, I will get the data for the latest update for the dev release, right? If I want to actually change my files, then I have to do a merge after the fetch. Is this correct?

Jean Gionet’s picture

Great tutorial! Thanks!

A Day In The Life

Roar’s picture

The guide left off just when it was getting interesting! Would love to learn how to handle the database with this workflow.

jasondaniels455’s picture

I definitely agree with you. Time to get Googling!

athampan’s picture

Hi All,

I consider myself an intermediate level user of Drupal and a n00b with git. I am involved in the development of a site with drupal setup as a multisite (single code base and multiple databases). The site is primarily focused on user-generated content (where uploaded files play a significant role) and these files can, if we get our user base, grow to very large numbers.

I am in a dilemma as to whether to gitignore the files (and hence achieve, I believe, a significant boost in server performance; backing up the files folder regularly using rsync) or, alternatively, to use git itself as a backup mechanism (therefore not using gitignore) facilitating easy rollback if (and when) necessary.

Would appreciate responses from the community and thanks in advance.

Best regards,

alex.designworks’s picture

Thanks for this great tutorial.

It would be great to add images with repos and arrows between them to represent actions for each command (or at least at the beginning) - that would remove any ambiguity between dev, staging and production environments.

Also, it is still unclear about using DB (I saw TODO about it) in branches. Maybe, using Backup and Migrate for every push to remote (through git hook) could solve this issue.

Director /Software Engineer | Integrated Experts |

jenlampton’s picture

I put up an image of how I manage my sites, which almost exactly matches this tutorial. Hope this helps :)

mkuhn’s picture

I wonder if the Drupal project could not use git in a way that makes it far more convenient to follow the latest release, namely by having a separate branch for only (say) 7.x releases.

At the moment, all development on the next 7.x release seems to be happening on the "7.x" branch, and when that development has completed, that branch gets a "7.13" (or whatever the next number) tag with its version number. This means that anyone who wants to follow the latest release has to look up the latest tag first and then do a checkout on that tag.

Would it not be much more convenient if there were separate "7.x-released" and "7.x-dev" branches, and whenever development on the "7.x-dev" branch has reached maturity for a new release, then the release is performed by merging "7.x-dev" into "7.x-released" (plus still adding a tag like "7.13" to the result of that merge to document the version number). This way, those of us who merely want to keep our production environments up-to-date with the latest release simply would have to initially do a "git checkout 7.x-released" to get the latest released 7.x version. After we have specified once what we want this way, we simply have to type "git pull" every now and then, to update to the very latest released 7.x version.

Any particular reason what that isn't done? It would seem much more convenient (especially in scripts), than having to manually specify the latest release tags each time.

kmccarthy’s picture

The tutorial is concise and well written, much appreciated. However as Drupal has just published a point release (7.14->7.15) I thought I'd try a test of the upgrade by merging the new version as described here. As I still had a copy of sites/default/default.settings.php that still had its permissions limited from the original install, git complained that it couldn't access the file and completely aborted. Resolving this and attempting another merge did not succeed as apparently many of the files in the merge had already been overwritten during the first attempt to that point and git aborted again. The only way I was able to set up a good merge was to make sure that the default.setting.php file was moved outside of the drupal tree to avoid the permissions issue altogether.
If anyone else runs into this, or knows of a better solution, or thinks I may have a more specific issue in what I described, I'd be happy to update the directions here to handle it.

mexicoder’s picture

Several people here have mentioned about diagrams and clarifications. I have put together a brief slideshow I'd appreciate any comments about how it can be improved as a resource. I also have a few suggestions for clarifications in this article

  • Add notes so you know whether you are on local or remote server
  • Diagram to show directory structure
  • Remove references to public_html, users and fooproject username (they are server subjective and not necessary for this example)
  • Rename directories and branches to something more semantic (fooprojectworkingtree) (fooprojectworkingbranch), this would give the new user much more idea about which folder, branch etc is doing what
  • Add some notes to get started or links as to which software etc you should be using
  • Troubleshooting/error messages

I am more than happy to make edits but thought I'd mention them for discussion before editing:

eljakeo’s picture

Add notes so you know whether you are on local or remote server <<<<<---------------- Please do this! I am pulling out my hair trying to figure this out and have wasted a whole day trying to get this to work, and I think my problem might lie in the confusion over which commands are on the server and which are local.

mexicoder’s picture

Hi eljakeo
did you take a look at the slideshow I posted- should help you out a lot. Appreciate any feedback

eljakeo’s picture

Thanks dumbass (and I mean that in the most respectable possible way!) The slideshow definitely helps. As I've worked with this tutorial and set up a few sites with it's methods, I'm actually finding it to be a great intro to Git overall. The other tutorials I've tried start out with too much detail. This is a great practical exercise that teaches you all the basics, at least as far as I'm concerned. Thanks again to you and everyone else who's contributed to this page and discussion thread.

izmeez’s picture

@mexicoder Thanks for this tutorial and the slides. The slides help provide more clarity.

I would suggest that slide #6 and onwards, if you can, possibly with different colors, show not just the directories but also the git branches.

Other than that a commendable job. Thanks.

firstlut’s picture

I'm working alone on a Drupal site, so I haven't done any branching.

  • I got the site in semi-presentable shape, then pushed to a remote on my shared host server one level above public_html.
  • I cloned to public_html/mysite, which is set up as a subdomain. Installed, loaded database. Everything looks fine.
  • I made some tweaks to a css file and added a couple of images in mytheme/images, so they're tracked.
  • Git sees them just fine. cPanel sees them just fine. When I open the CSS file on the server in Terminal, all my changes are there.

However, when I load the site in the browser, it still has the original colors, as if I never made any changes to the CSS file. I have cleared Drupal caches, cleared the browser caches, looked at it on another machine that had never been to that site before, and still: Bupkis.

I've reset, checked out by tag, and no change. Permissions on the changed CSS file are 644, same as all the CSS files that are being read.

I've been beating on this for two days, and I'm at my wit's end. Franz Kafka says this sh*t's jacked up.

mexicoder’s picture

It took me a while to get my head around exactly how git works and how code changes are pushed and pulled. I'd recommend starting with the basics- just follow the tutorial above step for step on a clean install of Drupal - in my experience sometimes jumping straight in with an existing site can add extra confusion. When you see exactly how the changes are made and what you can expect to happen on a clean Drupal install with git it will make your life a lot easier when it comes to working on your own site, it will also set up the default branches, remotes up etc.
In the tutorial above you are pushing from a branch in your local dev environment (git push remote) up to the remote repository then running a git pull on a branch (git pull remote) on the dev version of the site (in your case public_html/mysite).
As long as you are staging and committing your changes correctly (git add, git commit) then running git push and git pull I suspect your problems lie in either the set up of your branches or remotes, however as I say just running through the tutorial and taking a look at the slideshow I posted would be the best starting point.

firstlut’s picture

Thank you ever so much for your help, (not a) dumbass. Turns out my problem was not with git, the program, but that I am, in fact, a git.

I use Acquia Dev Desktop on my laptop, and it creates two parallel sites directories. The local site was reading one set of files, but the server was reading the other. I knew it was something derpy. It's been one of those weeks.

Your slides are really helpful, because everything git-related worked just fine. You should edit this page and add them in, because those visual representations of the process make it a lot easier to grasp.

koffer’s picture

this part

$ git remote rename origin drupal

why I need to rename to drupal if in all other part of intructions talk about a fooproject

When i type git remote -v. I get:

drupal git:// (fetch)
drupal git:// (push)
origin ssh://emailIuse/srv/www/fooproject.git/ (fetch)
origin ssh://emailIuse/srv/www/fooproject.git/ (push)

Web designer from mexico.

mexicoder’s picture

This is to reflect that you will not be pushing your changes to the core Drupal project but your own git project (in this case fooproject). Have you taken a look at my slides? They may help out as they give a visual representation of what is happening in the set up.

WorldFallz’s picture

I'm working through this now as part of setting up a brand new environment where I wanted to be able to leverage git_drupalorg in order to be able to quickly submit patches upstream. Since the cutover from CVS I have been unable to get a similar setup I had with CVS working with git and I notice that getting around to creating proper patches and submitting them sometimes just doesn't happen since I have to break out of my normal workflow to do it.

So, my #1 question is-- how are folks handling version display for the core update module? I have yet to find any acceptable solution that works. Using drush with --gitinfofile doesn't always work (some modules just don't get the right info, ctools being one)and also corrupts patches with the unnecessary version info. Also, neither git_deploy 7.x-1.x or 7.x-2.x appear to do anything either.

JayShoe’s picture

I'm using a drupalpro image so I'm on Ubantu. I follow the tutorial all the way up to the point where you git checkout -b fooproject. And this is where I get stuck.

At this point, you should complete the Drupal installation process to get a working local installation.

When I go to fooproject/ in my browser, I get a "This webpage is not available" ... "because the DNS lookup failed". What can I do to fix this error?

Thank you kindly for your help.

JayShoe’s picture


Still searching for the answer. I've also now just tried the following.

1. clone a git repository into
2. drush qc dns
3. cd
4. drush site-install --db-url...
5. Site installs properly.

But when I go into my browser and type I just get the "DrupalPro Development Desktop" with "Current phpinfo()". I'm using drupalpro with Ubantu.

Not seeming to have any luck being able to clone a git and get it to a WORKING site. I assume that I'm doing something wrong in the step with the dns (IE drush qc dns)!

Anyone have the solution for me?

mexicoder’s picture

Hi @JayShoe
I am a little unclear about your set up and what you are trying to do. The setup in the tutorial is based in the first instance (after creating the bare repository) on cloning the Drupal core project to a folder on an existing local development environment. In this case once you had cloned Drupal if you navigated to the fooproject directory on your local environment (e.g localhost/fooproject) from your web browser you'd see the usual drupal install page and you could take it from there. If you have another set-up you'd like to use it is sometimes best to run through this very simple setup first and have a play around with it then adapt it to your needs. I have a slideshow here which, when I get time, will add to the tutorial, which may help.

JayShoe’s picture

Hi @dumbass,

Thanks for taking the time to respond. I've selected to use DrupalPro as my development system on my local machine. I like the fact that it has most everything installed out of the box. And that I don't have to install a bunch of things onto my desktop and configure them in order to get up and running. I think it will come in handy to have this "portable" model in place. So I really like this method, but as you can see it's proving to have some challenges too.

In my development package (drupalpro) they recommend starting an instance with "quickstart-create". When I use quickstart-create to initialize an instance - I can happily access the site (although it does require a dot "." in the folder name to work and your examples aren't using one). But when I simply install a folder into this "websites" folder using steps contained in this tutorial - I can't access the site.

The problem is that when I go to load up a project in my browser which was NOT initialized via a $ drush quickstart-create, then I get a "Forbidden" or "This page is not available" page. So in this instance. I can follow your instructions all the way to where I've downloaded the clone, I've created my own branch, etc. But then I go to "initialize the install" in the browser, and I get the 'this page is not available".

I know that /fooprojects exists, it has the files, etc. I know it's in the right folder "websites". But there must be some difference on my machine that is required that is not mentioned in your tutorial.

So I started to think that I had to "initialize" some type of apache or dns setting. To get a site running I need apache, dns, database, and code. Right? So I said, OK let's do a command like $ drush qc apache dns database --domain=fooproject.test. This would create an empty folder which is theoretically setup to accept the code. So after this folder is setup, I do a command like $ git clone git:// fooproject.test. So I initialized the dns, apache, and database and then copied the code from git. I would have thought this would work, but no prevail. This is when I get the "Forbidden" error.

Bottom line is that I just simply can't LOAD the site with the browser.


visionquest’s picture

What is the reasoning for having both a local dev environment and a server dev environment when the server dev only pulls from the repo?

Is this simply to check that the changes on local dev work on the server or is there some other reason?

mexicoder’s picture

Hi @visionquest, it all depends on workflow really. If you are talking about only one developer, two dev environments is probably overkill. What you will find usually though is that you have teams of developers each with their own local development environment. They will commit code to the repo then pull it to the dev server environment to test various things like server issues, conflicts and a whole bunch of other stuff that can happen when working in teams. Dev is often quite throwaway, while staging is used for more stable code and to test against content. However there are no real hard and fast rules- the workflow described here is only one method to get you started.

LarsKramer’s picture

Thank you mexicoder! This was exactly the explanation I have been looking for.

Drupa1N00b’s picture

Hi. I've successfully managed to put this into my remote. However, when I clone my remote, even though the drupal files come back (from my remote obviously), it doesn't have the reference to the original Drupal repository anymore. That is, after a fresh clone, remote -v gives me this:

$ git remote -v 
origin	/my/remote/fooproject.git/ (fetch)
origin	/my/remote/fooproject.git/ (push)

Before I pushed to my remote, I had:

$ git remote -v 
drupal (fetch)
drupal (push)
origin	/my/remote/fooproject.git/ (fetch)
origin	/my/remote/fooproject.git/ (push)

That means if "git fetch drupal" won't work and neither would a subsequent updates to the Drupal core. This will make it easier for my devs so they know exactly where to fetch Drupal from.

Any idea where I can went wrong and how I can fix it? I won't mind starting from scratch.


mexicoder’s picture

No need to start from scratch just
git remote add drupal
You haven't broken anything it is just the fact in your original setup you were cloning from Drupal, which sets 'origin' as the drupal project (that you subsequently renamed to drupal), this time you were cloning from your remote which sets 'origin' as your remote project.

Drupa1N00b’s picture

But is there a way for my repository to remember this? That way, whoever does a clone they won't have to do git remote add drupal


mexicoder’s picture

That's more of a git question itself, it's not default behaviour and I haven't seen anything clean to do this or an approach I'd recommend, you'd also have to ask if you have a workflow where you have lots of people submitting code to a central repo whether you'd want one dev deciding to upgrade Drupal or whether you'd do that kind of thing more centrally

ericxb’s picture

I've been puzzling over the d8 ability to dump configurations and keeping those configurations in the project git tree. By default, those configs are saved in files. There are two problems: this info really shouldn't be in the http accessible tree at all; and the default .gitignore excludes the entirety of files so your config dumps are skipped. The dump location can be controlled in settings.php.

So how does one modify this "building drupal with git" to root in the directory which parents the drupal core dir?

Please see Building a d8 site with git for details and a solution which might be worth including on this page. I'm requesting review and corrections.

Johnnie Walker’s picture

I have noticed that this guide assumes that Drupal is installed in the root directory of my project. i.e in this scenario the project's README file will be overwritten by Drupal's README file. Which seems odd.

I tend to have a README file that reflects files of my own project in the root directory of my project. Also, it is nice to have the root directory of the project hold other project related files, such as build scripts, docs, data, configs etc.

I tend to store my website in a subdirectory of the project which contains the actual web docs (httpd).

I will have to do some more investigation to work out how to square this kind of set-up with this guide.

Johnnie Walker’s picture

Further to my previous comment...

I have just re-read the section on Subtree Merging In Chapter 9 of the Pro Git (2009) book by Scott Chacon.

It looks like it might be helpful in managing code from multiple sources.

delacosta456’s picture


haunted’s picture

Updating drupal core with "git fetch drupal" and "git merge 7.44", results in a bunch of conflicts.

Anyway, i find using drush simpler:

git checkout -b testupdate
drush up drupal
drush updatedb
git add -A
git commit -m "Upgraded drupal core to 7-x.x"

And if everything is fine, i merge the testupdate branch.

akupaka’s picture

As I was having some problems (glitches) with my "copied" site, I suggest reading this thread before configuring .gitignore -