How to deploy code
(→Alternative to scap: "# Sync" and "$ sync" were a bit too close) |
(format, clarify srv193 instructions) |
||
| (28 intermediate revisions by 6 users not shown) | |||
| Line 2: | Line 2: | ||
== Introduction == | == Introduction == | ||
| − | * All code is in version control ([https://gerrit.wikimedia.org/r/gitweb?p=operations/mediawiki-config.git;a=summary operations/mediawiki-config.git] repository) | + | * All code is in version control (in the [https://gerrit.wikimedia.org/r/gitweb?p=operations/mediawiki-config.git;a=summary operations/mediawiki-config.git] repository and others) |
| − | * This repository is checked out on [[fenari]] at <code>/ | + | * This repository is checked out on [[fenari]] at <code>/home/wikipedia/common</code>, with subdirectories for each branch of the MediaWiki codebase. |
| − | * [[sync scripts]] | + | * [[sync scripts]] synchronize that working copy on fenari onto <code>/usr/local/apache/common</code> on hundreds of servers. |
=== See also === | === See also === | ||
* [[test.wikipedia]] | * [[test.wikipedia]] | ||
* [[sync scripts]] | * [[sync scripts]] | ||
| − | * [[Heterogeneous deployment]] | + | * [[Heterogeneous deployment]] (covers other kinds of deployments) |
* [[git-deploy]] (a new deployment system design) | * [[git-deploy]] (a new deployment system design) | ||
== Basic common sense == | == Basic common sense == | ||
* Be careful. '''Breaking the site is surprisingly easy!''' | * Be careful. '''Breaking the site is surprisingly easy!''' | ||
| − | * If you're deploying code written by someone else, ask them to be around during deployment so they can troubleshoot if necessary | + | ** don't make deployment changes from a development directory, instead use a separate clean git clone just for deployments |
| − | * Make sure you know about anything hairy, such as additional prerequisites (e.g. schema changes) or potential complications when rolling back | + | ** check <kbd>git status</kbd> constantly (or set your shell prompt to show the info). |
| + | * If you're deploying code written by someone else, ask them to be around during deployment so they can troubleshoot if necessary. | ||
| + | * Make sure you know about anything hairy, such as additional prerequisites (e.g. schema changes) or potential complications when rolling back. | ||
| + | * Do the different operations in the most logical order. For example, if you're deploying a code impacting the databases, you should create or edit SQL tables before deploy a change requiring these tables. | ||
| + | * Join #wikimedia-operations and #wikimedia-tech on Freenode and be available before and after all changes. | ||
== Deployment requirements == | == Deployment requirements == | ||
| Line 24: | Line 28: | ||
* Common sense. See [[#Basic common sense| above]] | * Common sense. See [[#Basic common sense| above]] | ||
* Some shiny code | * Some shiny code | ||
| − | * A window of time to deploy during (that doesn't overlap with anyone else's window). | + | * A window of time to deploy during (that doesn't overlap with anyone else's window). [[Deployments]] is the calendar for planning and recording activities in these windows. |
| + | * A clean ''local'' git repository of mediawiki/core (use ssh for speed), in which you have set up git review using <kbd>git review -s</kbd> | ||
| + | * Be present on IRC. <tt>#wikimedia-tech</tt> and <tt>#wikimedia-operations</tt> are two places where people will come to yell at you if something goes wrong, you should be able to hear them. | ||
== Step 1: get the code in the deployment branch == | == Step 1: get the code in the deployment branch == | ||
| − | Before you can deploy anything, it has to be in the deployment branch. Our deployment branches are named wmf/1. | + | Before you can deploy anything, it has to be in the deployment branch(es). Our deployment branches are named wmf/1.''MAJOR''wmf''MINOR'' where <code>MAJOR</code> and <code>MINOR</code> are numbers that increase over time as new branches are cut. A new branch with an incremented MINOR number is cut at the start of each deployment cycle, and after each tarball release MAJOR is incremented and MINOR is reset to 1. Strict access control is enforced on the deployment branches, but you should have access to them if you are a deployer. On the cluster, the checkout of each deployment branch is in <code>/home/wikipedia/common/php-1.''MAJOR''wmf''MINOR''</code> . |
| + | The general status and schedule for each major release is in www.mediawiki.org/wiki/MediaWiki_1.''MAJOR''.''MINOR''/Roadmap , e.g. [[mw:MediaWiki_1.21/Roadmap]]. | ||
Note that in most cases the cluster will be running on two deployment branches, with some wikis running version N and some running version N+1. To see which wiki is running which version, inspect <code>/home/wikipedia/common/wikiversions.dat</code> ([http://noc.wikimedia.org/conf/wikiversions.dat.txt public mirror]) or look at [http://en.wikipedia.org/wiki/Special:Version Special:Version]. | Note that in most cases the cluster will be running on two deployment branches, with some wikis running version N and some running version N+1. To see which wiki is running which version, inspect <code>/home/wikipedia/common/wikiversions.dat</code> ([http://noc.wikimedia.org/conf/wikiversions.dat.txt public mirror]) or look at [http://en.wikipedia.org/wiki/Special:Version Special:Version]. | ||
| − | If your code or change needs to go live to all wikis, you will need to change all deployment branches that are in use. An easy way to see all of the versions currently in use is to log onto the staging server (fenari) and run | + | If your code or change needs to go live to all wikis, you will need to change all deployment branches that are in use. An easy way to see all of the versions currently in use is to log onto the staging server (fenari) and run <kbd>mwversionsinuse</kbd> from the command line. You can also run <kbd>mwversionsinuse --withdb</kbd> to see which wikis are running each version. |
'''NOTE:''' All examples on this page assume there is a single deployment branch called ''wmf/1.20wmf1'' checked out on the cluster in ''<code>php-1.20wmf1</code>''. You may need to adapt the examples to use a different branch name. If you are updating multiple deployment branches, simply repeat the steps for each deployment branch separately. | '''NOTE:''' All examples on this page assume there is a single deployment branch called ''wmf/1.20wmf1'' checked out on the cluster in ''<code>php-1.20wmf1</code>''. You may need to adapt the examples to use a different branch name. If you are updating multiple deployment branches, simply repeat the steps for each deployment branch separately. | ||
| Line 53: | Line 60: | ||
$ git checkout wmf/1.20wmf1 | $ git checkout wmf/1.20wmf1 | ||
$ git pull | $ git pull | ||
| − | $ git submodule update --init | + | $ git submodule update --init --recursive |
# Cherry-pick a commit from master, identified by its patch set hash | # Cherry-pick a commit from master, identified by its patch set hash | ||
| Line 91: | Line 98: | ||
$ git pull | $ git pull | ||
# Update the extension submodules. This may take a while when you run it for the first time | # Update the extension submodules. This may take a while when you run it for the first time | ||
| − | $ git submodule update --init | + | $ git submodule update --init --recursive |
# cd to your extension's submodule | # cd to your extension's submodule | ||
| Line 160: | Line 167: | ||
$ git pull | $ git pull | ||
# Update the extension submodules. This may take a while when you run it for the first time | # Update the extension submodules. This may take a while when you run it for the first time | ||
| − | $ git submodule update --init | + | $ git submodule update --init --recursive |
# cd to your extension's submodule | # cd to your extension's submodule | ||
| Line 193: | Line 200: | ||
You are deploying an update to your extension, updating it to the current master. If you want to apply changes to your extension but don't want to update it to exactly the current master state, read the [[#Case 1b: extension changes|extension changes]] section instead. | You are deploying an update to your extension, updating it to the current master. If you want to apply changes to your extension but don't want to update it to exactly the current master state, read the [[#Case 1b: extension changes|extension changes]] section instead. | ||
| − | All you need to do in this case is update the submodule in the core deployment branch | + | All you need to do in this case is update the submodule in the core deployment branch |
| − | + | ''(Note how your commit ID '96052d0' reappears in git output)'': | |
| − | + | ||
| − | # Set up a local wmf/1.20wmf1 branch that tracks the remote | + | $ '''cd mediawiki/core''' # Go to your checkout of core |
| − | # You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, you can skip this step | + | |
| − | # If you get an error, try 'git remote update' first | + | # Set up a local wmf/''1.20wmf1'' branch that tracks the remote |
| − | $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 | + | # You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, you can skip this step |
| − | Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. | + | # If you get an error, try 'git remote update' first |
| − | + | $ '''git branch --track wmf/''1.20wmf1'' origin/wmf/''1.20wmf1''''' | |
| − | # Switch to the wmf/1.20wmf1 branch and update it | + | Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. |
| − | $ git checkout wmf/1.20wmf1 | + | |
| − | $ git pull | + | # Switch to the wmf/1.20wmf1 branch and update it |
| − | # Update the extension submodules. This may take a while when you run it for the first time | + | $ '''git checkout wmf/''1.20wmf1''''' |
| − | $ git submodule update --init | + | $ '''git pull''' |
| − | + | # Update the extension submodules. This may take a while when you run it for the first time | |
| − | # cd to your extension's submodule | + | $ '''git submodule update --init --recursive''' |
| − | $ cd extensions/MyCoolExtension | + | |
| − | # Fetch the latest meta data from the remote server | + | # cd to your extension's submodule |
| − | $ git fetch | + | $ '''cd extensions/''MyCoolExtension''''' |
| − | # Point the submodule to a specific commit hash | + | # Fetch the latest meta data from the remote server |
| − | # Hint: Use a symbolic refname for convenience (e.g origin/master, gerrit/wmf/1.20wmf7 etc.) | + | $ '''git fetch''' |
| − | # Hint: The offset operator (~) works here, too. For the 2nd to last version of master use "origin/master~1". | + | # Point the submodule to a specific commit hash |
| − | $ git reset --hard origin/master | + | # Hint: Use a symbolic refname for convenience (e.g origin/master, gerrit/wmf/1.20wmf7 etc.) |
| − | HEAD is now at 96052d0... (bug 34885) Blah blah blah | + | # Hint: The offset operator (~) works here, too. For the 2nd to last version of master use "origin/master~1". |
| − | # Verify that you got the commit you intended. The repository might have changed in the | + | $ '''git reset --hard origin/master''' |
| − | + | HEAD is now at 96052d0... (bug 34885) Blah blah blah | |
| − | # Back to the main repo | + | # Verify that you got the commit you intended. The repository might have changed in the meantime, especially with offsets. |
| − | $ cd ../.. | + | |
| − | # Check that you have updated the submodule correctly | + | # Back to the main repo |
| − | # The diff should only show your submodule change, nothing else | + | $ '''cd ../..''' |
| − | $ git diff | + | # Check that you have updated the submodule correctly |
| − | diff --git a/extensions/MyCoolExtension b/extensions/MyCoolExtension | + | # The diff should only show your submodule change, nothing else |
| − | index 6a6eaab..96052d0 160000 | + | $ '''git diff''' |
| − | --- a/extensions/MyCoolExtension | + | diff --git a/extensions/MyCoolExtension b/extensions/MyCoolExtension |
| − | +++ b/extensions/MyCoolExtension | + | index 6a6eaab..96052d0 160000 |
| − | @@ -1 +1 @@ | + | --- a/extensions/MyCoolExtension |
| − | -Subproject commit 6a6eaabcdae29201c0f3e7bbcbac2d1953c574a5 | + | +++ b/extensions/MyCoolExtension |
| − | +Subproject commit 96052d0d9733f89d5f8fd43c6ba05ecc756ea1ec | + | @@ -1 +1 @@ |
| − | + | -Subproject commit 6a6eaabcdae29201c0f3e7bbcbac2d1953c574a5 | |
| − | # Commit the submodule update and submit it for review | + | +Subproject commit 96052d0d9733f89d5f8fd43c6ba05ecc756ea1ec |
| − | $ git commit -a -m "Update MyCoolExtension to master" | + | |
| − | # Submit your commit for review | + | # Commit the submodule update and submit it for review |
| − | $ git review | + | $ '''git commit -a -m "Update ''MyCoolExtension'' to master"''' |
| − | # If it gives you a warning about submitting more than one commit, you may want to do a 'git fetch gerrit' and retry | + | # Submit your commit for review |
| − | # If you don't want or need this to be reviewed, you can +2 your own | + | $ '''git review''' |
| − | # commit if you are in the wmf-deployment group | + | # If it gives you a warning about submitting more than one commit, you may want to do a 'git fetch gerrit' and retry |
| − | + | # If you don't want or need this to be reviewed, you can +2 your own | |
| + | # commit if you are in the wmf-deployment group | ||
Alternatively, you can use the script at [http://tstarling.com/stuff/update-submodule http://tstarling.com/stuff/update-submodule]. | Alternatively, you can use the script at [http://tstarling.com/stuff/update-submodule http://tstarling.com/stuff/update-submodule]. | ||
| Line 260: | Line 267: | ||
$ git pull | $ git pull | ||
# Update the extension submodules. This may take a while when you run it for the first time | # Update the extension submodules. This may take a while when you run it for the first time | ||
| − | $ git submodule update --init | + | $ git submodule update --init --recursive |
# Add a submodule for your extension | # Add a submodule for your extension | ||
| Line 296: | Line 303: | ||
== Step 2: get the code on fenari == | == Step 2: get the code on fenari == | ||
| − | Once the code is in the deployment branch, you simply run <code>git pull</code> on fenari to get it there: | + | Now switch to fenari. Use <kbd>ssh -A</kbd> so you can connect to gerrit and other hosts (or set up ssh proxying in your .ssh/config). |
| + | <pre> | ||
| + | catrope$ ssh -A fenari | ||
| + | </pre> | ||
| + | Once the code is in the deployment branch, you simply run <code>git pull</code> on fenari to get it there. | ||
| + | However, before doing so, make sure that no unexpected changes will show up (see [[#Problem: undeployed code]]). | ||
<pre> | <pre> | ||
catrope@fenari:~$ cd /home/wikipedia/common/php-1.20wmf1 | catrope@fenari:~$ cd /home/wikipedia/common/php-1.20wmf1 | ||
| + | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git fetch | ||
| + | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git log HEAD..origin/wmf/1.20wmf1 | ||
| + | </pre> | ||
| + | This will list the commits that would be pulled by 'git pull'. | ||
| + | If there are other changes besides yours, go yell at the culprit. | ||
| + | Otherwise you're OK to pull your changes into the deployment directory. | ||
| + | <pre> | ||
catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git pull | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git pull | ||
</pre> | </pre> | ||
| Line 304: | Line 323: | ||
If you're deploying changes or updates to an existing extension, you'll need to update the submodule separately: | If you're deploying changes or updates to an existing extension, you'll need to update the submodule separately: | ||
<pre> | <pre> | ||
| − | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git submodule update extensions/MyCoolExtension | + | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git submodule update --recursive extensions/MyCoolExtension |
</pre> | </pre> | ||
| + | ''you should see the commit ID from your work in your local deployment'' | ||
| − | If you're deploying a new extension, you have to explicitly tell | + | If you're deploying a new extension, you have to explicitly tell git to initialize the submodule: |
<pre> | <pre> | ||
| − | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git submodule update --init extensions/MyCoolExtension | + | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git submodule update --init --recursive extensions/MyCoolExtension |
</pre> | </pre> | ||
| − | + | If the wmf branch you just updated is the one that [http://test.wikipedia.org test.wikipedia.org] is on, then your code is now running on test.wikipedia.org and you can test it there. | |
| + | If, however, you have updated JavaScript/CSS files that do not get loaded with ResourceLoader, or are loaded with ResourceLoader in debug mode, you will need to sync them to testwiki: | ||
| + | <pre> | ||
| + | catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ ssh srv193 | ||
| + | catrope@srv193:~$ sync-common | ||
| + | </pre> | ||
| + | |||
| + | They will then be live on testwiki. | ||
| + | |||
| + | The cluster machines have a cache for I18n messages for each release that does not get updated by this; | ||
| + | it seems the only way to update these messages is to do a full scap. | ||
== Step 3: configuration and other prep work == | == Step 3: configuration and other prep work == | ||
| Line 361: | Line 391: | ||
If your change only touches one or a few files or directories and does not change i18n messages, you can sync the files/dirs individually with <code>[[sync-file]]</code> or <code>[[sync-dir]]</code> as appropriate, rather than having to run <code>[[scap]]</code>. This is preferable because a <code>scap</code> run always shakes the cluster up a bit and takes longer to complete, while a <code>sync-file</code> run is very lightweight. However, <code>sync-file</code> is only capable of synchronizing files within directories that already exist on the cluster, so it won't work with newly added directories. Also, <code>sync-file</code> only synchronizes one file at a time, and creates a log entry each time. Using it repetitively (e.g. with a for loop) to sync multiple files is fine, as long as there's not too many of them (say not more than ~5). | If your change only touches one or a few files or directories and does not change i18n messages, you can sync the files/dirs individually with <code>[[sync-file]]</code> or <code>[[sync-dir]]</code> as appropriate, rather than having to run <code>[[scap]]</code>. This is preferable because a <code>scap</code> run always shakes the cluster up a bit and takes longer to complete, while a <code>sync-file</code> run is very lightweight. However, <code>sync-file</code> is only capable of synchronizing files within directories that already exist on the cluster, so it won't work with newly added directories. Also, <code>sync-file</code> only synchronizes one file at a time, and creates a log entry each time. Using it repetitively (e.g. with a for loop) to sync multiple files is fine, as long as there's not too many of them (say not more than ~5). | ||
| − | To sync a single file, run <code>sync-file [path to file] [summary]</code>. To sync a directory, run <code>sync-dir [path to directory] [summary]</code>. The summary will be used by logmsgbot to log your sync in #wikimedia- | + | To sync a single file, run <code>sync-file [path to file] [summary]</code>. To sync a directory, run <code>sync-dir [path to directory] [summary]</code>. The summary will be used by logmsgbot to log your sync in #wikimedia-operations, from where it'll go to the [[server admin log]] and the identi.ca and Twitter feeds. |
| − | * '''PITFALL''': The path argument has to be relative to the <code>common</code> directory, not to the current directory. To preserve your sanity (and tab-completion functionality), I recommend you always cd to <code>common</code> before running <code>sync-file</code> | + | * '''PITFALL''': The path argument has to be relative to the <code>common</code> directory, not to the current directory. To preserve your sanity (and tab-completion functionality), I recommend you always cd to <code>/home/wikipedia/common</code> before running <code>sync-file</code> |
* '''PITFALL''': If the summary argument contains spaces, you'll have to put it in quotes or only the first word is used. If your summary contains a <code>$</code>, you'll either have to escape it or put your summary in single quotes, to prevent bash's variable expansion from messing it up | * '''PITFALL''': If the summary argument contains spaces, you'll have to put it in quotes or only the first word is used. If your summary contains a <code>$</code>, you'll either have to escape it or put your summary in single quotes, to prevent bash's variable expansion from messing it up | ||
* '''PITFALL''': <code>sync-file</code> and <code>sync-dir</code> do not work correctly for syncing i18n changes. They will appear to work, but the i18n changes won't take effect. To sync i18n changes, you must use <code>[[scap]]</code>. | * '''PITFALL''': <code>sync-file</code> and <code>sync-dir</code> do not work correctly for syncing i18n changes. They will appear to work, but the i18n changes won't take effect. To sync i18n changes, you must use <code>[[scap]]</code>. | ||
| Line 382: | Line 412: | ||
=== More complex changes: sync everything === | === More complex changes: sync everything === | ||
| − | If you're adding directories, changing many files, changing i18n messages, or otherwise have a reason why <code>sync-file</code> wouldn't work or would be impractical, you'll have to run <code>scap</code>, which syncs everything and rebuilds caches. <code>scap</code> logs to the server admin log, and reports in #wikimedia- | + | If you're adding directories, changing many files, changing i18n messages, or otherwise have a reason why <code>sync-file</code> wouldn't work or would be impractical, you'll have to run <code>scap</code>, which syncs everything and rebuilds caches. <code>scap</code> logs to the server admin log, and reports in #wikimedia-operations (without !log) when it finishes. |
<pre> | <pre> | ||
awjrichards@fenari:/home/wikipedia/common/php$ scap 'Log message here' | awjrichards@fenari:/home/wikipedia/common/php$ scap 'Log message here' | ||
| Line 396: | Line 426: | ||
</pre> | </pre> | ||
| − | Running <code>scap</code> can take | + | Running <code>scap</code> can take upwards of <s>15</s> 60 minutes; the LocalisationCache rebuilds (usually two of them, one for each deployed wmf version) can also take a while. There is usually a load spike and a few hiccups on the cluster immediately after scapping, but that's normal, as long as it subsides a few minutes after <code>scap</code> finishes running. |
=== Alternative to scap === | === Alternative to scap === | ||
| Line 417: | Line 447: | ||
== Don't leave town == | == Don't leave town == | ||
| − | Even if your deploy appears to be working, it's important to be reachable in the hours immediately following your deploy. Ideally, stay online and in IRC channels like #wikimedia-tech for a couple of hours. | + | Even if your deploy appears to be working, it's important to be reachable in the hours immediately following your deploy. Ideally, stay online and in IRC channels like #wikimedia-tech for a couple of hours. Update [[Deployments]] with what happened in your deployment window. |
| + | |||
If you must go offline, let people know how to reach you (and keep your mobile phone or other communications device on your person). You can use /away messages on IRC, or perhaps send a short email to private-l. | If you must go offline, let people know how to reach you (and keep your mobile phone or other communications device on your person). You can use /away messages on IRC, or perhaps send a short email to private-l. | ||
| Line 426: | Line 457: | ||
Since we have [[mw:Extension:ResourceLoader|ResourceLoader]], there is no need to e.g manually do a "build" (to re-minify/re-cache static files). ResourceLoader does this automatically on-demand. Depending on when the timestamp cache gets a cache-miss, it can take up to five minutes for that to occur. | Since we have [[mw:Extension:ResourceLoader|ResourceLoader]], there is no need to e.g manually do a "build" (to re-minify/re-cache static files). ResourceLoader does this automatically on-demand. Depending on when the timestamp cache gets a cache-miss, it can take up to five minutes for that to occur. | ||
| + | |||
| + | == Problem: undeployed code == | ||
| + | Roan commented in October 2012: | ||
| + | |||
| + | The problem is that sometimes, people merge things into a deployment branch and | ||
| + | then don't deploy them. This is a terrible habit that should be | ||
| + | squashed. If you merge something into a wmf branch, you have a | ||
| + | responsibility to either deploy it yourself very soon, make sure that | ||
| + | someone deploys it very soon, or revert it if you can't make those | ||
| + | things happen. The deployment branch should reflect the current state | ||
| + | of the cluster, except during those brief moments where something is | ||
| + | about to be deployed or in the process of being deployed. | ||
| + | |||
| + | If you are concerned about other commits being pulled in (which | ||
| + | ''should'' never happen, unless someone has been naughty), | ||
| + | then in [[#Step_2:_get_the_code_on_fenari|Step 2]] you can run | ||
| + | <kbd>git fetch</kbd> followed by <kbd>git log HEAD..origin/wmf/1.20wmf1</kbd>. | ||
| + | This will list the commits that would be pulled by 'git pull'. | ||
| + | In that list, it should be easy to spot commits that aren't yours and identify the | ||
| + | person to yell at. If you run 'git pull' and it ends up pulling things | ||
| + | you didn't expect, you can use 'git log' to examine what happened, and | ||
| + | 'git reflog' (or the output of 'git pull') to find the hash of the | ||
| + | commit you were at before pulling, so you can roll back to it if | ||
| + | needed. But if this happens to you, feel free to start yelling at | ||
| + | people and/or asking for help. | ||
[[Category:How-To]] | [[Category:How-To]] | ||
Latest revision as of 00:40, 23 February 2013
This document SHOULD be up-to-date for the git migration. If something is wrong in this document, poke Roan.
[edit] Introduction
- All code is in version control (in the operations/mediawiki-config.git repository and others)
- This repository is checked out on fenari at
/home/wikipedia/common, with subdirectories for each branch of the MediaWiki codebase. - sync scripts synchronize that working copy on fenari onto
/usr/local/apache/commonon hundreds of servers.
[edit] See also
- test.wikipedia
- sync scripts
- Heterogeneous deployment (covers other kinds of deployments)
- git-deploy (a new deployment system design)
[edit] Basic common sense
- Be careful. Breaking the site is surprisingly easy!
- don't make deployment changes from a development directory, instead use a separate clean git clone just for deployments
- check git status constantly (or set your shell prompt to show the info).
- If you're deploying code written by someone else, ask them to be around during deployment so they can troubleshoot if necessary.
- Make sure you know about anything hairy, such as additional prerequisites (e.g. schema changes) or potential complications when rolling back.
- Do the different operations in the most logical order. For example, if you're deploying a code impacting the databases, you should create or edit SQL tables before deploy a change requiring these tables.
- Join #wikimedia-operations and #wikimedia-tech on Freenode and be available before and after all changes.
[edit] Deployment requirements
- Cluster account request through RT; request deployment access (requires manager and/or sr dev approval)
- If you can ssh into fenari, and ssh into a random srv box (e.g. srv300) from there, you already have this.
- Deployment branch access requested
- Common sense. See above
- Some shiny code
- A window of time to deploy during (that doesn't overlap with anyone else's window). Deployments is the calendar for planning and recording activities in these windows.
- A clean local git repository of mediawiki/core (use ssh for speed), in which you have set up git review using git review -s
- Be present on IRC. #wikimedia-tech and #wikimedia-operations are two places where people will come to yell at you if something goes wrong, you should be able to hear them.
[edit] Step 1: get the code in the deployment branch
Before you can deploy anything, it has to be in the deployment branch(es). Our deployment branches are named wmf/1.MAJORwmfMINOR where MAJOR and MINOR are numbers that increase over time as new branches are cut. A new branch with an incremented MINOR number is cut at the start of each deployment cycle, and after each tarball release MAJOR is incremented and MINOR is reset to 1. Strict access control is enforced on the deployment branches, but you should have access to them if you are a deployer. On the cluster, the checkout of each deployment branch is in /home/wikipedia/common/php-1.MAJORwmfMINOR .
The general status and schedule for each major release is in www.mediawiki.org/wiki/MediaWiki_1.MAJOR.MINOR/Roadmap , e.g. mw:MediaWiki_1.21/Roadmap.
Note that in most cases the cluster will be running on two deployment branches, with some wikis running version N and some running version N+1. To see which wiki is running which version, inspect /home/wikipedia/common/wikiversions.dat (public mirror) or look at Special:Version.
If your code or change needs to go live to all wikis, you will need to change all deployment branches that are in use. An easy way to see all of the versions currently in use is to log onto the staging server (fenari) and run mwversionsinuse from the command line. You can also run mwversionsinuse --withdb to see which wikis are running each version.
NOTE: All examples on this page assume there is a single deployment branch called wmf/1.20wmf1 checked out on the cluster in php-1.20wmf1. You may need to adapt the examples to use a different branch name. If you are updating multiple deployment branches, simply repeat the steps for each deployment branch separately.
NOTE: Also, all git examples assume you have a clean working copy, that is, you have no uncommitted changes. To verify this, run git status, it should say nothing added to commit (working directory clean) or nothing added to commit but untracked files present . If you are doing git-fu with a dirty working copy, there is a high probability you will screw things up, so don't do that unless you know what you're doing.
[edit] Case 1a: core changes
You are deploying changes to MediaWiki core. This should be rare because core is updated from master every two to three weeks, but in some cases it might be necessary. For core changes, you will simply need to push or submit changes to the wmf/1.20wmf1 branch in core. The most common use case is to take a commit that is already in the repository somewhere (usually in master, sometimes a commit that's still pending review) and cherry-pick it into the deployment branch, so only that case is documented below.
To cherry-pick a commit into the deployment branch, do the following things locally:
$ cd mediawiki/core # go to your checkout of mediawiki/core.git # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, # you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it from the remote $ git checkout wmf/1.20wmf1 $ git pull $ git submodule update --init --recursive # Cherry-pick a commit from master, identified by its patch set hash $ git cherry-pick ffb1b38ad83927606c539ac941e9f3eb2653a840 # If there are conflicts, this is how you fix them: # - run 'git status' to see which files are conflicted # - start fixing conflicted files using your favorite editor # - use 'git add filename' to tell git you've fixed the conflicts in a file # - once all conflicts are resolved, commit the result using 'git commit' # Submit your cherry-pick commit for review $ git review # If you don't want or need this to be reviewed, you can +2 your own # commit if you are in the wmf-deployment group
[edit] Case 1b: extension changes
You are deploying changes to an extension, but you don't just want to deploy master. Instead, you want to deploy the code that is currently deployed, plus your change. If you do want to just update your extension to master, read the extension update section instead.
Extensions have deployment branches too, but they are created on an as-needed basis, so your extension may or may not have a deployment branch with the correct name. If it doesn't exist yet, you'll have to create it. You can check by running git branch -a and checking if remotes/origin/wmf/1.20wmf1 or remotes/gerrit/wmf/1.20wmf1 appears in the list.
[edit] Creating an extension deployment branch
If your extension does not yet have a deployment branch, or doesn't have one with the correct name (e.g. there might be a 1.20wmf1 branch but used for an earlier deploy, but you're deploying to 1.20wmf3), you will need to create one. If your extension does have a correctly named deployment branch, you can skip this section.
First, find out the commit hash of the currently deployed commit:
$ cd mediawiki/core # Go to your checkout of core # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it $ git checkout wmf/1.20wmf1 $ git pull # Update the extension submodules. This may take a while when you run it for the first time $ git submodule update --init --recursive # cd to your extension's submodule $ cd extensions/MyCoolExtension # Look at the most recent commit $ git log -1 commit 434a740ef8dcd244029efd720a8e211e2803d343 # <---THIS Author: Reedy <reedy@wikimedia.org> Date: Wed Apr 11 14:45:57 2012 +0100
Special:Version also lists the deployed commit hash for each extension, in shorthand form (e.g. 434a740), you can use that to double-check.
Now, create a new branch based off that commit:
$ cd mediawiki/extensions/MyCoolExtension # Go to the extension checkout # Create a new branch and switch to it # Make sure you use the commit ID, not the change ID $ git checkout -b wmf/1.20wmf1 434a740ef8dcd244029efd720a8e211e2803d343 Switched to a new branch 'wmf/1.20wmf1' # Update .gitreview $ vim .gitreview # Change 'defaultbranch=master' to 'defaultbranch=wmf/1.20wmf1' $ git commit .gitreview -m 'Create wmf/1.20wmf1 branch and update .gitreview' # Push the branch into gerrit $ git push origin wmf/1.20wmf1 # Let the local branch track the remote branch $ git branch --set-upstream wmf/1.20wmf1 origin/wmf/1.20wmf1
[edit] Now that your extension has a deployment branch
Just like in core, the most common use case for updating a deployment branch is to cherry-pick changes from master:
$ cd mediawiki/extensions/MyCoolExtension # go to your extension checkout # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it from the remote $ git checkout wmf/1.20wmf1 $ git pull # Cherry-pick a commit from master, identified by its hash $ git cherry-pick 176ffdd3b71e463d3ebaa881a6e77b82acba635d # If there are conflicts, this is how you fix them: # run 'git status' to see which files are conflicted # start fixing conflicted files # use 'git add filename' to tell git you've fixed the conflicts in a file # once all conflicts are resolved, commit the result using 'git commit' # Submit your commit for review $ git review # If you don't want or need this to be reviewed, you can +2 your own # commit if you are in the wmf-deployment group
Additionally, however, you will need to update the submodule in core:
$ cd mediawiki/core # Go to your checkout of core # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it $ git checkout wmf/1.20wmf1 $ git pull # Update the extension submodules. This may take a while when you run it for the first time $ git submodule update --init --recursive # cd to your extension's submodule $ cd extensions/MyCoolExtension # Update it from the remote $ git fetch # Point the submodule to the remote wmf/1.20wmf1 branch $ git checkout origin/wmf/1.20wmf1 HEAD is now at 96052d0... (bug 34885) Blah blah blah # cd back to the main repo $ cd ../.. # Check that you have updated the submodule correctly # The diff should only show your submodule change, nothing else $ git diff diff --git a/extensions/MyCoolExtension b/extensions/MyCoolExtension index 6a6eaab..96052d0 160000 --- a/extensions/MyCoolExtension +++ b/extensions/MyCoolExtension @@ -1 +1 @@ -Subproject commit 6a6eaabcdae29201c0f3e7bbcbac2d1953c574a5 +Subproject commit 96052d0d9733f89d5f8fd43c6ba05ecc756ea1ec # Commit the submodule update and submit it for review $ git commit -a -m "Update MyCoolExtension" $ git review # If you don't want or need this to be reviewed, you can +2 your own # commit if you are in the wmf-deployment group
[edit] Case 1c: extension update
You are deploying an update to your extension, updating it to the current master. If you want to apply changes to your extension but don't want to update it to exactly the current master state, read the extension changes section instead.
All you need to do in this case is update the submodule in the core deployment branch (Note how your commit ID '96052d0' reappears in git output):
$ cd mediawiki/core # Go to your checkout of core # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, you can skip this step # If you get an error, try 'git remote update' first $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it $ git checkout wmf/1.20wmf1 $ git pull # Update the extension submodules. This may take a while when you run it for the first time $ git submodule update --init --recursive # cd to your extension's submodule $ cd extensions/MyCoolExtension # Fetch the latest meta data from the remote server $ git fetch # Point the submodule to a specific commit hash # Hint: Use a symbolic refname for convenience (e.g origin/master, gerrit/wmf/1.20wmf7 etc.) # Hint: The offset operator (~) works here, too. For the 2nd to last version of master use "origin/master~1". $ git reset --hard origin/master HEAD is now at 96052d0... (bug 34885) Blah blah blah # Verify that you got the commit you intended. The repository might have changed in the meantime, especially with offsets. # Back to the main repo $ cd ../.. # Check that you have updated the submodule correctly # The diff should only show your submodule change, nothing else $ git diff diff --git a/extensions/MyCoolExtension b/extensions/MyCoolExtension index 6a6eaab..96052d0 160000 --- a/extensions/MyCoolExtension +++ b/extensions/MyCoolExtension @@ -1 +1 @@ -Subproject commit 6a6eaabcdae29201c0f3e7bbcbac2d1953c574a5 +Subproject commit 96052d0d9733f89d5f8fd43c6ba05ecc756ea1ec # Commit the submodule update and submit it for review $ git commit -a -m "Update MyCoolExtension to master" # Submit your commit for review $ git review # If it gives you a warning about submitting more than one commit, you may want to do a 'git fetch gerrit' and retry # If you don't want or need this to be reviewed, you can +2 your own # commit if you are in the wmf-deployment group
Alternatively, you can use the script at http://tstarling.com/stuff/update-submodule.
[edit] Case 1d: new extension
You are adding an entirely new extension that wasn't deployed before, and you're deploying from master (if you need to deploy something other than the master state, that's possible, but it generally shouldn't be done for an initial deployment; master should just be clean and deployable).
You need to add a submodule to the core deployment branch:
$ cd mediawiki/core # Go to your checkout of core
# Set up a local wmf/1.20wmf1 branch that tracks the remote
# You only need to do this once for each branch; if you've already got a wmf/1.20wmf1 branch, you can skip this step
$ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1
Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin.
# Switch to the wmf/1.20wmf1 branch and update it
$ git checkout wmf/1.20wmf1
$ git pull
# Update the extension submodules. This may take a while when you run it for the first time
$ git submodule update --init --recursive
# Add a submodule for your extension
$ git submodule add https://gerrit.wikimedia.org/r/p/mediawiki/extensions/MyCoolExtension.git extensions/MyCoolExtension
Cloning into extensions/MyCoolExtension...
# Check the diff. Make sure the .gitmodules entry is in line with the others, and check that the subproject commit hash points to master
$ git diff --cached
diff --git a/.gitmodules b/.gitmodules
index 3ab3d48..9a4cc66 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -346,3 +346,6 @@
[submodule "extensions/PrefSwitch"]
path = extensions/PrefSwitch
url = https://gerrit.wikimedia.org/r/p/mediawiki/extensions/PrefSwitch.git
+[submodule "extensions/MyCoolExtension"]
+ path = extensions/MyCoolExtension
+ url = https://gerrit.wikimedia.org/r/p/mediawiki/extensions/MyCoolExtension.git
diff --git a/extensions/AllTimeZones b/extensions/MyCoolExtension
new file mode 160000
index 0000000..46727ad
--- /dev/null
+++ b/extensions/MyCoolExtension
@@ -0,0 +1 @@
+Subproject commit 46727ad74adda33621323deb2bebdc2527cb4917
# Commit the submodule addition and submit it for review
$ git commit -a -m "Add MyCoolExtension"
$ git review
# If you don't want or need this to be reviewed, you can +2 your own
# commit if you are in the wmf-deployment group
After adding a new extension to the deployment branch, you also have to add it to make-wmf-branch/default.conf in git (https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/tools/release.git) (in the $normalExtensions array) so it'll be picked up when the deployment branch is rebranched.
[edit] Step 2: get the code on fenari
Now switch to fenari. Use ssh -A so you can connect to gerrit and other hosts (or set up ssh proxying in your .ssh/config).
catrope$ ssh -A fenari
Once the code is in the deployment branch, you simply run git pull on fenari to get it there.
However, before doing so, make sure that no unexpected changes will show up (see #Problem: undeployed code).
catrope@fenari:~$ cd /home/wikipedia/common/php-1.20wmf1 catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git fetch catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git log HEAD..origin/wmf/1.20wmf1
This will list the commits that would be pulled by 'git pull'. If there are other changes besides yours, go yell at the culprit. Otherwise you're OK to pull your changes into the deployment directory.
catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git pull
If you're deploying changes or updates to an existing extension, you'll need to update the submodule separately:
catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git submodule update --recursive extensions/MyCoolExtension
you should see the commit ID from your work in your local deployment
If you're deploying a new extension, you have to explicitly tell git to initialize the submodule:
catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ git submodule update --init --recursive extensions/MyCoolExtension
If the wmf branch you just updated is the one that test.wikipedia.org is on, then your code is now running on test.wikipedia.org and you can test it there. If, however, you have updated JavaScript/CSS files that do not get loaded with ResourceLoader, or are loaded with ResourceLoader in debug mode, you will need to sync them to testwiki:
catrope@fenari:/home/wikipedia/common/php-1.20wmf1/$ ssh srv193 catrope@srv193:~$ sync-common
They will then be live on testwiki.
The cluster machines have a cache for I18n messages for each release that does not get updated by this; it seems the only way to update these messages is to do a full scap.
[edit] Step 3: configuration and other prep work
In certain cases, you'll have to change how Wikimedia sites are configured. We generally have the same codebase everywhere, but with different configurations for each wiki.
Maybe you are just changing one configuration variable. Or, perhaps you are adding a brand-new extension, or activating an extension on some wiki where it's never been before. For all of these cases, and more, you'll have to make the changes to the config files to get the desired results.
Configuration files live in their own revision-controlled repository. Everything that follows is just a convenient way to make those changes.
If you're deploying an extension or feature that can be switched off, it's usually best to leave it switched off while you deploy and carefully switch it on after that using a simple configuration change (this is called a dark launch). Even if you do this, you should build any configuration infrastructure (e.g. $wmg variable, adding entry in InitialiseSettings with default false) at this time so all you'll have to do later is flip a switch.
For specific preparations, see the sections below as well as How to do a schema change and How to do a configuration change. Best to perform schema changes before making config changes.
[edit] Add a configuration switch for an extension
In /home/wikipedia/common/wmf-config/CommonSettings.php, add:
if ( $wmgEnableMyExtension ) { require_once( "$IP/extensions/MyExtension/MyExtension.php" ); // Set config vars if needed // If you want to export config vars through InitialiseSettings.php, you need to set $wmgMyExtensionThingy there and do #$wgMyExtensionThingy = $wmgMyExtensionThingy; }
In /home/wikipedia/common/wmf-config/InitialiseSettings.php, add something like:
'wmgEnableMyExtension' => array( 'default' => false, 'eswikibooks' => true, // etc. ), // If needed, set $wmgMyExtensionWhatever vars here too
If your extension requires a large-ish amount of configuration, consider putting it in a separate file instead. Currently, AbuseFilter, LiquidThreads and FlaggedRevs do this.
For more documentation on these files and their formats, see Configuration files.
[edit] Add new extensions to extension-list
When adding a new extension, you need to add it to the extension list, or its i18n messages won't get picked up. For more information about this setup, see this.
- cd to
/home/wikipedia/common/wmf-config - Edit
extension-listand add the path to the extension setup file on a line by itself - Run scap. Make sure your extension is only enabled on testwiki at this point
[edit] Step 4: synchronize the changes to the cluster
[edit] Small changes: sync individual files
If your change only touches one or a few files or directories and does not change i18n messages, you can sync the files/dirs individually with sync-file or sync-dir as appropriate, rather than having to run scap. This is preferable because a scap run always shakes the cluster up a bit and takes longer to complete, while a sync-file run is very lightweight. However, sync-file is only capable of synchronizing files within directories that already exist on the cluster, so it won't work with newly added directories. Also, sync-file only synchronizes one file at a time, and creates a log entry each time. Using it repetitively (e.g. with a for loop) to sync multiple files is fine, as long as there's not too many of them (say not more than ~5).
To sync a single file, run sync-file [path to file] [summary]. To sync a directory, run sync-dir [path to directory] [summary]. The summary will be used by logmsgbot to log your sync in #wikimedia-operations, from where it'll go to the server admin log and the identi.ca and Twitter feeds.
- PITFALL: The path argument has to be relative to the
commondirectory, not to the current directory. To preserve your sanity (and tab-completion functionality), I recommend you always cd to/home/wikipedia/commonbefore runningsync-file - PITFALL: If the summary argument contains spaces, you'll have to put it in quotes or only the first word is used. If your summary contains a
$, you'll either have to escape it or put your summary in single quotes, to prevent bash's variable expansion from messing it up - PITFALL:
sync-fileandsync-dirdo not work correctly for syncing i18n changes. They will appear to work, but the i18n changes won't take effect. To sync i18n changes, you must usescap.
When running sync-file or sync-dir, you'll usually see about half a dozen errors from broken servers. We should really fix things some time so this doesn't happen, but in the meantime you can consider this completely normal (sample output below). It usually completes within a few seconds, but in rare cases it may hang on a broken Apache for 1 or 2 minutes.
catrope@fenari:/home/wikipedia/common$ sync-file php-1.20wmf1/api.php 'API security fix' No syntax errors detected in /home/wikipedia/common/php-1.20wmf1/api.php copying to apaches mw60: ssh: connect to host mw60 port 22: Connection timed out srv189: ssh: connect to host srv189 port 22: Connection timed out srv174: ssh: connect to host srv174 port 22: Connection timed out srv162: ssh: connect to host srv162 port 22: Connection timed out srv206: ssh: connect to host srv206 port 22: Connection timed out srv284: ssh: connect to host srv284 port 22: Connection timed out srv281: ssh: connect to host srv281 port 22: Connection timed out srv266: ssh: connect to host srv266 port 22: Connection timed out
[edit] More complex changes: sync everything
If you're adding directories, changing many files, changing i18n messages, or otherwise have a reason why sync-file wouldn't work or would be impractical, you'll have to run scap, which syncs everything and rebuilds caches. scap logs to the server admin log, and reports in #wikimedia-operations (without !log) when it finishes.
awjrichards@fenari:/home/wikipedia/common/php$ scap 'Log message here' Checking syntax... Copying to fenari...Done. Updating serialized data files... Warning: messages are no longer serialized by this makefile. Updating ExtensionMessages-1.19.php... Updating ExtensionMessages-1.20wmf1.php... Updating LocalisationCache for 1.19... Updating LocalisationCache for 1.20wmf1... ...snip...
Running scap can take upwards of 15 60 minutes; the LocalisationCache rebuilds (usually two of them, one for each deployed wmf version) can also take a while. There is usually a load spike and a few hiccups on the cluster immediately after scapping, but that's normal, as long as it subsides a few minutes after scap finishes running.
[edit] Alternative to scap
Because scap takes such an incredibly long time these days, here's an alternative set of commands you can use if you want to deploy i18n changes but want to avoid running scap:
# Rebuild the i18n cache $ mw-update-l10n # Sync the i18n changes. The first parameter is the version number. If updating both versions, run this twice $ sync-l10nupdate-1 1.20wmf12 # Sync your code $ sync-dir php-1.20wmf12/extensions/Whatever
[edit] Test your code live
Is it doing what you expected? Unfortunately, testwiki is not exactly like the real wikis. Consider that extensions can activate hooks. Things like CentralNotice or Common.js might effect the browser environment as well. No one environment can simulate all the wikis that we operate, so test your change afterwards on a live wiki to confirm.
[edit] Don't leave town
Even if your deploy appears to be working, it's important to be reachable in the hours immediately following your deploy. Ideally, stay online and in IRC channels like #wikimedia-tech for a couple of hours. Update Deployments with what happened in your deployment window.
If you must go offline, let people know how to reach you (and keep your mobile phone or other communications device on your person). You can use /away messages on IRC, or perhaps send a short email to private-l.
If you are on Wikimedia staff, now might be a great time to check if your contact info is up to date. If you aren't on staff, ask a staffer to add your contact info to that page, under "Important volunteers".
[edit] A note on JavaScript and CSS
Since we have ResourceLoader, there is no need to e.g manually do a "build" (to re-minify/re-cache static files). ResourceLoader does this automatically on-demand. Depending on when the timestamp cache gets a cache-miss, it can take up to five minutes for that to occur.
[edit] Problem: undeployed code
Roan commented in October 2012:
The problem is that sometimes, people merge things into a deployment branch and then don't deploy them. This is a terrible habit that should be squashed. If you merge something into a wmf branch, you have a responsibility to either deploy it yourself very soon, make sure that someone deploys it very soon, or revert it if you can't make those things happen. The deployment branch should reflect the current state of the cluster, except during those brief moments where something is about to be deployed or in the process of being deployed.
If you are concerned about other commits being pulled in (which should never happen, unless someone has been naughty), then in Step 2 you can run git fetch followed by git log HEAD..origin/wmf/1.20wmf1. This will list the commits that would be pulled by 'git pull'. In that list, it should be easy to spot commits that aren't yours and identify the person to yell at. If you run 'git pull' and it ends up pulling things you didn't expect, you can use 'git log' to examine what happened, and 'git reflog' (or the output of 'git pull') to find the hash of the commit you were at before pulling, so you can roll back to it if needed. But if this happens to you, feel free to start yelling at people and/or asking for help.