Difference between revisions of "Git for Wesnoth Crash Course"

From The Battle for Wesnoth Wiki
m (The ultimate cleanup power tool: git rebase)
(link cleanup)
 
(32 intermediate revisions by 6 users not shown)
Line 1: Line 1:
Foreword by iceiceice: This page is intended for new wesnoth devs and contributors who have never used git before. I'm hoping that it will be written *mainly* by newbies like myself who have just figured out enough to use it successfully, and therefore not contain a bunch of information superfluous for that purpose. At the time that I am writing this, I have managed to write about 8 github pull requests and get them merged into wesnoth, and I hope you will be able to do the same after reading this.
+
== Foreword ==
  
== What is git? ==
+
'''''(An essay by, mainly, iceiceice.)'''''
  
The most effective *absolute first timer* intro to git that I found when I began is here: http://gitref.org/index.html
+
This page is intended for new <cite>Battle for Wesnoth</cite> developers and contributors who have never used '''[[WesnothRepository|<cite>Git</cite>]]''' before. I'm hoping that it will be written <em>mainly</em> by newbies like myself who have just figured out enough about <cite>Git</cite> to use it successfully, and therefore not include a bunch of information superfluous for that purpose. At the time that I am writing this, I have managed to write about eight GitHub pull requests and get them merged into <cite>Wesnoth</cite>, and I hope you will be able to do the same after reading this.
  
I suggest that you read through this cover to cover, flipping through the pages using the red arrows at the bottom of the page. You can skip the part about "stashing", but you should read carefully and understand the stuff about:
+
== What is <cite>Git</cite>? ==
  
* cloning a repo
+
The most effective '''"absolute beginner" introduction to <cite>Git</cite>''' that I found when I started is here: '''[http://gitref.org <cite>gitref.org</cite>]'''. - ''(Note: this gitref.org link seems to have died.  Another resource, that is only slightly less introductory-level, is '''[https://git-scm.com/book/en/v2 <cite>Pro-Git Book</cite>]'''.  Don't worry about reading the whole thing though.)''
* checking out a branch, creating a new branch, merging branches
 
* adding and committing changes
 
* taking diffs to make sure you understand exactly what you are committing
 
* resetting when you messed up and need to go back a few steps, and using git log and git reflog to assist with this
 
* moving content between local and remote locations, with push and pull
 
  
== Setting up our workflow for wesnoth in git ==
+
I suggest that you <strong>read this through</strong>, beginning to end, navigating through the pages by using the red arrows at the bottom of each page. You can skip the part about ''"stashing"'', but you should carefully read and understand the parts about:
  
It is common in reading about git to see the terms "upstream / downstream". This is often confusing at the beginning -- after all information is usually flowing in both directions, sometimes you are getting code from the wesnoth repo, and sometimes you are sending code back. Which way is up / down?
+
* '''''cloning''''' a ''repository'';
 +
* '''''checking out''''' a ''branch'', creating a '''new branch''', and '''merging branches''';
 +
* '''''adding''''' and '''''committing changes''''';
 +
* viewing '''''diffs''''' to make sure you understand exactly what you are committing;
 +
* '''''resetting''''' your repository state in case you make a mistake and need to undo some steps, and using "'''<kbd>git log</kbd>'''" and "'''<kbd>git reflog</kbd>'''" to assist with this; and
 +
* moving content between local and remote repositories, with "'''<kbd>push</kbd>'''" and "'''<kbd>pull</kbd>'''".
  
The answer is that like a river, "downstream" is the direction most information flows. Any user that simply wants to build wesnoth from source, and not make any changes, will generally do so by cloning / pulling https://github.com/wesnoth/wesnoth. So wesnoth is the upstream repo, and the users are downstream. Since you are a developer, you will be forking the upstream repo and making changes, and hopefully with work eventually get these changes pulled into the upstream repo. That is the development cycle, and sending info back upstream is what makes you a developer.
+
== Setting up our workflow for <cite>Wesnoth</cite> in <cite>Git</cite> ==
  
In this guide we are going to assume that you will use github besides just the git command line. The reason for this is that github has a lot of nice tools to offer and makes some things really easy, especially with comparing your branch to master and seeing diffs in a nice graphical interface, and with making pull requests. Technically, this means you will actually control *two* repos -- the local one on your machine, and a fork of the main wesnoth repo, on github. The first step is to set these two things up. Follow the instructions here:
+
It is common when reading about <cite>Git</cite> to see the terms '''''"upstream"''''' and '''''"downstream"'''''. This is often confusing at the beginning -- after all, information is usually flowing in both directions; sometimes, you are getting code from the <cite>Wesnoth</cite> repository, and sometimes you are sending code to it. Which way is up and which is down?
  
https://help.github.com/articles/fork-a-repo
+
Analogously to a river, ''downstream'' is the direction toward which most information flows. Any user that simply wants to build <cite>Wesnoth</cite> from source, and not make any changes to it, will generally do so by cloning or pulling from <https://github.com/wesnoth/wesnoth>. So [https://github.com/wesnoth/wesnoth <code>wesnoth/wesnoth</code>] (the main <cite>Wesnoth</cite> repository) is the <dfn>upstream</dfn> repository, and the users are <dfn>downstream</dfn>.
  
So the steps are:
+
If you are a developer, you will '''''fork''''' the upstream repository and edit the source code, and -- hopefully, with work, eventually -- get your edits pulled into the upstream repository. That is the '''<dfn>development cycle</dfn>''', and contributing changes back to upstream is what makes you a developer.  
# Fork the main wesnoth repo and tie the fork to your user account.
 
# Clone the fork onto your local machine, using 'git clone'.
 
# Configure remotes.<br>The github article describes how to configure the main wesnoth repo under the name "upstream". Because we cloned from your fork, the fork is automatically configured under the name "origin". You can see your configuration status with 'git remotes'.
 
  
You might find this naming scheme confusing, as the name "origin" might also aptly describe the main wesnoth repo. So it might be a good idea to reconfigure that repo under the name "fork". But if you do, you should keep in mind that much of the github help documentation will assume the name "origin".
+
In this guide, we are going to assume that you will use GitHub in addition to the official <cite>Git</cite> command-line program, because GitHub has many useful tools to offer and makes some things nicely easy, especially when comparing a topic branch to the master branch and seeing diffs in a somewhat more advanced interface, and when making ''pull requests''. Technically, this means that you will have '''<em>two</em> repositories''' -- your <dfn>local repository</dfn> on your computer, and a ''fork'' of the <code>wesnoth/wesnoth</code> repository, on GitHub.
  
Additionally, we ask that you configure your actual full name into git, following instructions here:
+
The first step of becoming a <cite>Wesnoth</cite> developer is to set up these two repositories. Follow the instructions in [https://help.github.com/articles/fork-a-repo/ GitHub's "<cite>Fork A Repo</cite>" help article], which may be summarized as follows:
  
https://help.github.com/articles/setting-your-username-in-git
+
<ol>
 +
<li><p>'''''Fork''''' the <code>wesnoth/wesnoth</code> repository, using the GitHub website.</p></li>
 +
<li><p>'''''Clone''''' the fork onto your computer, with the command "<kbd>git clone</kbd>".</p></li>
 +
<li><p>Configure '''<dfn>remotes</dfn>''' (''remote'' repositories that are essentially bookmarked).</p>
 +
<p>GitHub's <cite>Fork A Repo</cite> article describes how to set the <code>wesnoth/wesnoth</code> repository as a remote with the name "<code>upstream</code>". Because you cloned from your fork, the fork is automatically set as a remote with the name "<code>origin</code>". You can see your remotes with the command "<kbd>git remote -v</kbd>".</p>
 +
<p>You might find this naming scheme confusing, given that the name "<code>origin</code>" might also aptly describe the <code>wesnoth/wesnoth</code> repository. So it might be good to rename that remote to "<code>fork</code>" (with the command "<kbd>git remote rename origin fork</kbd>"). But if you do that, note that much of the GitHub help pages will assume the name "<code>origin</code>".</p></li>
 +
</ol>
  
so that we can associate an actual person to every commit that makes it into the game. Your github account should also have your real name as well.
+
Additionally, we ask that you configure <cite>Git</cite> to use your full real name or a consistent alias, per the instructions in [https://help.github.com/articles/setting-your-username-in-git/ GitHub's "<cite>Setting your username in Git</cite>" help article], so that we can associate a concrete person with every commit merged into <cite>Wesnoth</cite>. Your GitHub profile should also display the same name.
  
 
== The basic workflow ==
 
== The basic workflow ==
  
Here's the picture we now have, with 3 repos:
+
Here's the picture we now have, with 3 repositories:
  
  
Line 46: Line 49:
 
   
 
   
 
   
 
   
                     origin
+
                     origin (fork)
 
   
 
   
 
   
 
   
Line 53: Line 56:
  
  
There are three basic steps in the development cycle.
+
There are four basic steps in the development cycle, which are as follows:
  
# Syncing with upstream and making a topic branch for your patch.
+
# ensuring that your local repository is '''up-to-date''' with the upstream repository;
# Committing your changes and pushing them to your fork.
+
# creating a '''topic branch''' for your changes;
# Making a pull request, getting it pulled, and cleaning up at the end.
+
# '''committing''' your changes to the topic branch and '''pushing''' them to your fork; and
 +
# filing a GitHub '''pull request''', having that request granted, then finally cleaning up.
  
=== Syncing and Branching ===
+
=== Keeping your local repository up-to-date ===
  
In this step, you will make sure your local master is up to date with the most recent development changes before beginning work.  
+
In this step, you will make sure your local <code>master</code> branch is up-to-date with the most recent development changes before beginning work.  
  
First make sure you are on master:
+
First make sure you are on your <code>master</code> branch:
  
 
   git checkout master
 
   git checkout master
 
   git status
 
   git status
  
Now, pull the upstream master to your machine:
+
Now, pull the upstream <code>master</code> branch to your local repository:
  
  
Line 74: Line 78:
 
       |
 
       |
 
       |
 
       |
       |            origin
+
       |            origin (fork)
 
       |           
 
       |           
 
       v       
 
       v       
Line 83: Line 87:
 
   git pull upstream master
 
   git pull upstream master
  
At this point your local master is up to date. While you are at it, you might as well sync up your *origin* as well:
+
At this point your local master is up to date. While you are at it, you might as well update your fork as well:
  
  
Line 90: Line 94:
 
          
 
          
 
          
 
          
                    origin
+
                .--> origin (fork)
               /-->  
+
               /   
            --/     
+
              /     
     local    
+
     local --'
  
  
 
   git push origin master
 
   git push origin master
  
So at this point, master should be the same everywhere. In this guide, we will never make any changes directly to master, and will *only* make changes to the topic branch, so no matter which repo you look at, master should be the same, and git will be comparing your work against the main wesnoth master.
+
At this point, the <code>master</code> branch should be the same in all three repositories.
 +
 
 +
=== Creating a topic branch ===
 +
 
 +
In this guide, we will <strong><em>never</em> make any changes directly on the <code>master</code> branch</strong>, and will <strong><em>only</em> make code changes on a topic branch</strong>, so regardless of which repository you look at, the <code>master</code> branch should be the same, and <cite>Git</cite> will be comparing your work against the upstream <code>master</code> branch.
  
Now we are ready to branch. We are on master, as a call to "git status" will confirm. So we will make our new branch:
+
Now you are ready to create a new '''[https://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches <dfn>topic branch</dfn>]''', which will hold your work for this patch. We are on the <code>master</code> branch, as running "<kbd>git status</kbd>" will confirm. Create your new topic branch -- named, in this example, "<code>new-feature</code>" -- by running the following command:
  
   git checkout -b great_new_feature
+
   git checkout -b new-feature
  
As you learned in the gitref guide, you could also have done this by
+
As shown in the <cite>gitref.org</cite> guide, you could also have done this with the following commands:
  
   git branch great_new_feature
+
   git branch new-feature
   git checkout great_new_feature
+
   git checkout new-feature
  
Since we were on master just before we did this, the current most up-to-date master will be the point of departure for our branch, which is best to avoid conflicts when it is eventually merged in.
+
Given that you were on the <code>master</code> branch when you created the <code>new-feature</code> topic branch, the <code>new-feature</code> branch will branch off from the ''tip'' of your current <code>master</code> branch, which is best, to avoid conflicts when the <code>new-feature</code> branch is eventually merged upstream.
  
 
=== Committing your changes and pushing to your fork ===
 
=== Committing your changes and pushing to your fork ===
  
Now, you will make your changes to wesnoth and test them, using 'git add' and 'git commit', as you learned in the guide.
+
Now, you will make your changes to <cite>Wesnoth</cite>, test them, and commit them to your local repository, with the commands "<kbd>git add</kbd>" and "<kbd>git commit</kbd>", as shown in the <cite>gitref.org</cite> guide.
 +
 
 +
==== Committing guidelines ====
 +
 
 +
<ol>
 +
<li>
 +
<h5 style="font-size: larger"><strong>Write good commit messages</strong></h5>
 +
 
 +
<p><strong>A commit message should fully explain what changes were made in the commit, and <em>why</em> those changes were made</strong>. A good commit message is structured like an email message.</p>
 +
 
 +
<p>The first line, which <strong>summarizes</strong> the commit, has special importance -- to quote the <cite>[[DeveloperGuide]]</cite> page, <q cite="[[DeveloperGuide#Commit_messages]]">This is the first (and often the only) line someone using browsing tools will see</q>. It should ideally be at most 80 characters (TODO: inconsistent with current policy), and is like the subject line of an email message. Commit message summary lines should be written in the imperative present tense, e.g., "Add field to <var>some_class</var>, with accessor methods", "Fix <var>other_class</var>'s constructor", "Delete unnecessary #include", etc.</p>
 +
 
 +
<p>After the summary line, there must be a blank line, after which comes the '''commit message body''', which is analogous to the body of an email message.</p>
 +
 
 +
<p>'''Optimally''', a commit's message would be so clearly and comprehensively explanatory that it would answer any questions that anyone might have about the commit, now, the next day, or years into the future -- which is especially important because the author of the commit likely won't always be around to answer those questions.</p>
 +
 
 +
<p>See also [[DeveloperGuide#Commit_messages|the <cite>DeveloperGuide</cite> page's "Commit messages" section]] and [https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines the official Git book's "Commit Guidelines" section].</p>
 +
</li>
 +
<li>
 +
<h5 style="font-size: larger"><strong>Make appropriately sized commits</strong></h5>
 +
 
 +
<p>Each commit should correspond to a <strong>single logical step</strong> in your programming task. The commits constitute the project's development history; people will look at your commits to try to figure out how we got to where we are today, and may at some point need to undo the changes made in some of your commits to fix a future issue.</p>
 +
 
 +
<p><strong>If you find it difficult to write a succinct commit message that fully explain what changes were made in the commit, and why those changes were made, then the commit is insufficiently fine-grained -- too large and too complicated.</strong> Another guideline is, you should at least think about committing almost every time you compile (depending on your habits).</p>
 +
 
 +
<p>On the other hand, you shouldn't make a commit for every line of code typed. <strong>If it would never make sense to revert the repository's code to its current state from a future state, you probably shouldn't commit.</strong> For example, if you define a field of an object, but it has no accessors and no way to be used in code, it would never make sense to revert to that time in the repository's history, so those changes should be saved together in a single commit. If you have a commit that merely adjusts whitespace in a file, that should be squashed in with another commit, according to current (2015-07-25) policy.</p>
  
Rules of thumb:
+
<p><strong>It is better to commit more often than less often</strong>, because, before you submit your changes to upstream, you will have the opportunity to edit your commit history, and <strong>it is much easier to ''squash'' commits together than to split up an overly-large commit</strong>.</p>
  
# Write a good commit message.<br><br>Basically, a commit message explains what happened and why in the commit. A good commit message is structured like a short email. The first line has special importance -- it should ideally be at most 80 characters, and play the roll of the "subject" of your email. These should be written in the imperative tense e.g. "add field to object and create accessor methods", "fix broken constructor", "remove unnecessary #include", etc. Skip a line after this line, and put the "body" of the email after it. Small commits often won't have a body in the commit message, but every commit must have a commit message.<br><br>
+
<p>See also [[DeveloperGuide#Commits|the <cite>DeveloperGuide</cite> page's "Commits" section]].</p>
# Make appropriately sized commits.<br><br>Each commit should correspond to a single logical step in your programming task. The commits are what make up the development history -- people will look at your commits to try to figure out how we got to where we are today, and if necessary, may need to revert some commits / revert to an earlier state to fix something that might be broken in the future. So one rule of thumb is, if you can't write a succinct commit message explaining what happened, then the commit is too large and too complicated, so break it up into fewer pieces. Another guideline is, you should at least think about committing almost every time you compile (depending on your habits).<br><br>On the other hand, you shouldn't make a commit for every line of code typed. Generally if it would never make sense to revert to the current point in code, you probably shouldn't commit. So for example, if you define a field of an object, but it has no accessors and no way to be used in code, it would never make sense to revert to that time in history, so those changes should be rolled together in one commit. Also, if you have a commit that just consists of "fixed some whitespace", that should probably be squashed in with another commit.<br><br>It is better to commit more often than less often, as before you are done you will have the opportunity to clean things up a bit, and it is much easier to squash commits together than to break up a commit that is too big.<br><br>See also additional remarks here: [[DeveloperGuide]]<br><br>
+
</li>
# When you are done, make a changelog entry, and commit it with the message "update changelog".<br><br>We also have a players_changelog, which should contain only changes players are likely to notice, and be less technical than the main technical changelog. There is also a file RELEASE_NOTES which basically gets turned into a forum post when the next release comes around, so if there is a major feature which should be advertised, that should go in there. Additionally, if this is your first commit, the devs will ask you to make an appropriate note about yourself in data/about.cfg<br><br>
+
<li>
 +
<h5 style="font-size: larger"><strong>Keep the change-logs, release notes, and credits up-to-date</strong></h5>
 +
 
 +
<p>After committing your changes, add an entry in the '''<code>changelog</code>''' document, and commit it with the message "Update changelog".</p>
 +
 
 +
<p>We also have a '''<code>players_changelog</code>''' document, which should contain only changes that most players are likely to notice, and be less technical than the main, technical <code>changelog</code>.</p>
 +
 
 +
<p>There is also a '''<code>RELEASE_NOTES</code>''' document, which basically is copied into the next <cite>Wesnoth</cite> release's release announcement post in the forums, so if there is a new feature or bug fix that should be advertised, add that to <code>RELEASE_NOTES</code>.</p>
 +
 
 +
<p>If this is your first commit, the development team will ask you to add an appropriate note about yourself in the '''credits document''', '''<code>data/about.cfg</code>'''.</p>
 +
 
 +
<p>See also [[DeveloperGuide#Changelogs_and_release_notes|the <cite>DeveloperGuide</cite> page's "Changelogs and release notes" section]].</p>
 +
</li>
 +
</ol>
  
 
Finally, when your branch is ready, push it to your fork.
 
Finally, when your branch is ready, push it to your fork.
Line 128: Line 174:
 
          
 
          
 
          
 
          
                    origin
+
                .--> origin (fork)
               /--> 
+
               /
            --/  
+
              /
     local    
+
     local --'
  
  
   git push origin great_new_feature
+
   git push origin new-feature
  
  
Now, if you navigate to the page for your fork, you will see "recently pushed branch great_new_feature (3 minutes ago)" on your main master page. Click "compare" to compare it against master. You will now see a list of the commits you made with their messages, and a color-highlighted diff of all the files you modified. You can click through the commits in order to see each of the changes you made and understand how your project progressed -- this is exactly what the devs will do when they review your pull request.
+
Now, if you navigate to your fork's page on GitHub, you should see a message such as "recently pushed branch new-feature (3 minutes ago)". Click "Compare" to compare it against the <code>master</code> branch. You should now see a list of the commits you made on the <code>new-feature</code> branch, and color-highlighted diffs of the files you changed. You can click through the commits in order to see each of the changes you made and understand how your project progressed -- this is exactly what the development team will do when they review your pull request.
  
In the github interface, you can always see a list of branches by clicking on "branches" in the line near the middle of the page  
+
In the GitHub interface, you can always see a list of branches by clicking on "branches" in the line near the middle of the page that looks like the following:
  
"10,000+ commits    27 branches    248 releases    85 contributors"
+
10,000+ commits    27 branches    248 releases    85 contributors
  
You can select the current branch using the drop down menu, and you can compare branches using the "compare" button.
+
You can also look at a specific branch using the "Branch" menu below that line, and you can compare branches using the green "Compare" button to the left of the "Branch" menu.
  
 
=== Cleaning up your branch ===
 
=== Cleaning up your branch ===
  
Before making your pull request, if you looked at the diffs and the history and saw anything confusing or not quite right, now is your opportunity to change it. From the gitref guide, you know how to add more commits, and you know that you can use reset to go back in time and redo things. Reset is especially good if your last commit was too big and you want to break it up into a series of smaller commits -- the command 'git reset HEAD^' will leave your working directory the same, but jump back in time one commit and unstage all of those changes, so you can add a smaller subset of the files, commit those, then add the others, etc.
+
Before filing your pull request, if, looking at the diffs and the history (the list of commits) for your topic branch, you saw anything confusing or not quite right, now is your opportunity to change it.
  
More generally, using 'git reflog' you can see how to reset back any number of commits / commands.  
+
From the <cite>gitref.org</cite> guide, you should know how to add more commits, and you should know that you can use "<kbd>reset</kbd>" to go back in time and redo things.
  
You can also use 'git commit --amend' to edit the commit message of the most recent commit.
+
"'''<kbd>reset</kbd>'''" is especially good if your last commit was too big and you want to break it up into a series of smaller commits -- the command "<kbd>git reset "@~"</kbd>" will leave your working directory the same, but jump back in time one commit and unstage all of those changes, so you can add a smaller subset of the files, commit those, then add the others, etc.
  
There are several more advanced cleanup features available with git, but we'll defer any further discussion.
+
More generally, you can use "'''<kbd>git reflog</kbd>'''" to see how to reset back over any number of commits or other <cite>Git</cite> operations.  
  
Every time you make changes, you will want to push to your origin to sync them up again. If you rewrote your history, then the push will be rejected, because the histories won't match up. Therefore, you will have to *force push* which will discard the old history and replace it with the new one:
+
You can also use "'''<kbd>git commit --amend</kbd>'''" to edit the commit message of the most recent commit.
 +
 
 +
There are several more advanced cleanup features available with <cite>Git</cite>, but we'll defer any further discussion of them.
 +
 
 +
Every time you make changes, you should to push them to your fork to keep it up-to-date. If you rewrote your commit history, then the push would be rejected, because the commit histories won't match. Therefore, to push a rewritten history, you must '''''<strong>force push</strong>''''', which will -- <strong>this is a potentially dangerous operation!</strong> -- discard the old commit history and replace it with the new history:
  
  
Line 162: Line 212:
 
            
 
            
 
          
 
          
                    origin
+
                .--> origin (fork)
               /--
+
               /
            --/  
+
              /
    local   
+
    local --'
 +
 
 +
 
 +
  git push --force origin new-feature
 +
 
 +
You can and should do this throughout your development if you want to look at the diffs of your changes as you work -- after all, the <cite>Wesnoth</cite> code-base is vast and it is easy to forget exactly where you are in your project. You can and should keep cleaning up in this way until you are satisfied with the result. Doing this, your GitHub fork ends up being rather like a personal Web-based IDE plug-in or extension to help you with development.
  
 +
=== Filing a pull request ===
  
  git push --force origin great_new_feature
+
When you finish working on your feature or bug fix, select your topic branch on your fork (with the "Branch" menu), and click the green button to file a pull request. By default, the target of the pull request -- the repository and branch into which it is requesting that your changes be pulled -- will be "<samp>wesnoth:master</samp>", the <code>master</code> branch of the main <cite>Wesnoth</cite> repository.
  
You can and should do this throughout your development if you want to look at the diffs of your changes as you work -- after all, the wesnoth source code is vast and it is easy to forget exactly where you are in your project. And you can keep cleaning up this way until you are satisfied. In this way, your github fork ends up being sort of like a personal web-based IDE plug-in / extension to help you with development.
+
The message that you enter for the pull request should become the commit message of the merge operation performed in the upstream repository if the pull request is granted. (Note: if you are granting someone's PR through the GitHub Web interface, please ensure that this happens.)
  
=== Making a pull request ===
+
If you fixed a bug, you should say, e.g., "Fixed bug #5678" in the pull-request message, which should automatically link to [https://gna.org/bugs/index.php?group=wesnoth&set=custom&report_id=101&status_id=1&chunksz=150&boxoptionwanted=1 our Gna! bug tracker].
  
When you are finally done, select your branch on your fork, and click the green button to create a pull request. By default, the target will be wesnoth:master, the main repo. That's pretty much all there is to it -- github has a guide here: https://help.github.com/articles/creating-a-pull-request
+
If you want more information, you could read [https://help.github.com/articles/creating-a-pull-request/ GitHub's "<cite>Creating a pull request</cite>" help article].
  
  
  
     upstream
+
     upstream <-.
            <--\
+
                \
                 \--
+
                 \
                    origin
+
                  '- origin (fork)
 
    
 
    
 
    
 
    
Line 188: Line 244:
  
  
You'll then see your pull request appear in the "pull requests" section of the main wesnoth repo. At the time of writing this, we also have "travis-ci", a "continuous integration system" configured on our repo. Travis will automatically try to compile wesnoth with your changes, as a reality check before your pull request is merged. It is not mandatory for the travis build to pass though, and additionally you may still make changes to your pull request by pushing more changes to your fork. Travis *should* then try to compile the updated list of changes after this. You can even still force push to erase the content of the PR and replace it with new content -- however you shouldn't make your PR until you are ready for it to be merged.
+
You should then see your pull request in [https://github.com/wesnoth/wesnoth/pulls the main <cite>Wesnoth</cite> repository's "pull requests" page].
 +
 
 +
At the time of writing this, we also have configured [https://travis-ci.org Travis CI], a [https://en.wikipedia.org/wiki/Continuous_integration continuous integration] system, to automatically try to compile <cite>Wesnoth</cite> with your changes, to make sure that it still works before your pull request is granted. However, it is not mandatory for the Travis build to complete successfully for the pull request to be granted. Additionally, you may still make changes to your pull request by pushing more changes to your topic branch in your fork, after which Travis <em>should</em> try to compile the updated topic branch. You can even still force push to erase the content of the topic branch (on which the pull request is based) and replace it with new content -- however, you shouldn't file a pull request for a topic branch until you are ready for it to be merged.
  
Now you just have to wait for a member of the development team to review your pull request. You can often find someone on the irc channel, #wesnoth-dev. Additionally devs may comment directly on your PR with questions, so you might check it periodically for this.
+
Now, wait for a member of the development team to review your pull request. You can often find someone on the development [[Support#IRC|IRC]] channel, <code>#wesnoth-dev</code>. Additionally, developers may comment directly on your pull request with questions, so you should check it periodically.
  
 
=== Cleaning up at the end ===
 
=== Cleaning up at the end ===
  
Congratulations, your PR got merged! Now its time to cleanup and resync with the new changes. On the github page for your PR, you will now see a button with the option "delete this branch". Since the content of great_new_feature is now merged into master, this branch no longer needs to exist as you won't work on it anymore, and future work will go onto a different branch. So you should delete it, which will delete the branch on *origin*, your github-associated repo.
+
Congratulations, a developer granted your pull request! Now it's time to clean up and update your local repository.
 +
 
 +
On the GitHub page for your pull request, you should now see a button with the option "delete this branch". Since the content of new-feature is now merged into master, this branch no longer needs to exist as you won't work on it anymore, and future work will go onto a different branch. So you should delete it, which will delete the branch on *origin*, your github-associated repo.
  
 
Since master has now changed and we want master to be synced up, you should now do step 1 again:
 
Since master has now changed and we want master to be synced up, you should now do step 1 again:
Line 202: Line 262:
 
       |
 
       |
 
       |
 
       |
       |            origin
+
       |            origin (fork)
 
       |           
 
       |           
 
       v       
 
       v       
Line 211: Line 271:
 
   git pull upstream master
 
   git pull upstream master
  
At this point git will understand that great_new_feature has been merged into master, so when you ask to delete this branch from local as well, it will do it:
+
At this point git will understand that new-feature has been merged into master, so when you ask to delete this branch from local as well, it will do it:
  
   git branch -d great_new_feature
+
   git branch -d new-feature
  
 
If you do these steps out of order, i.e. try to delete the branch before before syncing master, it will make a warning "Are you sure? You have unmerged changes on this branch..." etc.
 
If you do these steps out of order, i.e. try to delete the branch before before syncing master, it will make a warning "Are you sure? You have unmerged changes on this branch..." etc.
Line 224: Line 284:
 
            
 
            
 
            
 
            
                     origin
+
                     origin (fork)
 
               /-->   
 
               /-->   
 
             --/     
 
             --/     
Line 238: Line 298:
 
       |    <--\
 
       |    <--\
 
       |        \--
 
       |        \--
       |            origin
+
       |            origin (fork)
 
       |      /-->   
 
       |      /-->   
 
       v    --/     
 
       v    --/     
Line 257: Line 317:
 
Either way, afaik the vast majority of wesnoth developers prefer to push directly to wesnoth:master.
 
Either way, afaik the vast majority of wesnoth developers prefer to push directly to wesnoth:master.
  
An extreme option appropriate for small changes is to skip the local repo as well, and simply commit changes directly from the github web interface. This might be appropriate for example if you forgot to commit a changelog entry in an earlier patch, or are simply changing some numbers used as parameters somewhere. Obviously if you change source code this way, you should make certain that everything still compiles immediately after!
+
An extreme option appropriate for small changes is to skip the local repo as well, and simply commit changes directly from the github web interface. This might be appropriate for example if you forgot to commit a changelog entry in an earlier patch, or are simply changing some numbers used as parameters somewhere. Obviously if you change source code this way, you should make certain that everything still compiles immediately after! (Note: Don't actually do that.)
  
 
== The ultimate cleanup power tool: git rebase ==
 
== The ultimate cleanup power tool: git rebase ==
Line 267: Line 327:
 
* squashing commits together
 
* squashing commits together
 
* editing the content changes represented by an individual commit
 
* editing the content changes represented by an individual commit
* rewrite/edit the commit message
+
* editing the commit message
  
 
For our purposes, we assume you are working on a topic branch, and then you will only need to use the form
 
For our purposes, we assume you are working on a topic branch, and then you will only need to use the form
Line 290: Line 350:
 
When you become comfortable with git rebase -i, it will greatly improve your workflow, as you will know that you can easily review and revise commit messages later, and easily reorder and squash commits. Typically I will now commit every single time I compile, and even if it is a commit which e.g. fixes a compile time error, or for some other reason I know it will be squashed in somewhere else, I give it the commit message "fixup", as a note to myself to mark it thusly in the first git rebase pass. (Actually, while writing this I have just learned about the --autosquash feature of git rebase which takes this idea further, good stuff there.)
 
When you become comfortable with git rebase -i, it will greatly improve your workflow, as you will know that you can easily review and revise commit messages later, and easily reorder and squash commits. Typically I will now commit every single time I compile, and even if it is a commit which e.g. fixes a compile time error, or for some other reason I know it will be squashed in somewhere else, I give it the commit message "fixup", as a note to myself to mark it thusly in the first git rebase pass. (Actually, while writing this I have just learned about the --autosquash feature of git rebase which takes this idea further, good stuff there.)
  
As a final note, git rebase can also be used to help resolve merge issues, should they arise. For example, suppose that you modify a file in the same place as someone else, and their change gets merged before yours. If git can't figure out how to merge then someone will have to fix it. One way that you could fix it is to resync your master to get the new changes locally, and then rebase your topic branch so that your changes are applied *after* the most recent changes.
+
Note that unlike Linus and github, in this guide we aren't thinking about your github fork repo as public -- we prefer to think of it as your personal private IDE/tool as I described earlier. If other wesnoth devs are cloning it or pulling your topic branches, we assume you will work it out with them. With that caveat the philosophies expressed on github and by Linus should generally apply to the wesnoth project as well.
 +
 
 +
=== Solving merge conflicts with git rebase ===
 +
 
 +
Github version: https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request
 +
 
 +
Suppose that you modify a file in the same place as someone else, and their change gets merged before yours. If git can't figure out how to merge, then someone will have to fix it. This could be done by whoever is merging the new content into master, but if you don't have commit access then that person is not you. One way that *you* could fix it is to resync your master to get the new changes locally, and then rebase your topic branch so that your changes are applied *after* the most recent changes.
  
To understand what is happening, we should understand a bit more about how git works. Intuitively, git is all about "snapshots" of the project content. Every commit represents a snapshot, and you can look at this snapshot by checking out the commit. However git does not store each snapshot separately -- instead git defines the snapshots in terms of one another, and stores only the diffs. When you rebase your topic branch onto master, this will also update the "point of departure" where your branch was created, so that in the history, it will depart from the current head of master with the most recent changes. If merging your branch with the current master would create a conflict, then when git rebase gets to the commit that creates the conflict, it will get confused when it tries to apply that diff, stop, tell you about the problem, and ask you to resolve it -- you can open up the offending file and go to the point where the conflict is happening, and git will leave a note for you of the content it is having trouble with. The note will look similar to what happens in the gitref guide here, under "merge conflicts".
+
  git rebase master
 +
 
 +
To understand what is happening, we should understand a bit more about how git works. Intuitively, git is all about "snapshots" of the project content. Every commit represents a snapshot, and you can look at this snapshot by checking out the commit. However git does not store each snapshot separately -- instead git defines the snapshots in terms of one another, and stores only the diffs. When you rebase your topic branch onto master, this will also update the "point of departure" where your branch was created, so that in the history, it will depart from the current head of master with the most recent changes. (Obviously, if you don't sync master until you are done working, then this subtlety to 'git rebase' is irrelevant.)
 +
 
 +
If merging your branch with the current master would create a conflict, then when git rebase gets to the commit that creates the conflict, it will get confused when it tries to apply that diff, stop, tell you about the problem, and ask you to resolve it -- you can open up the offending file and go to the point where the conflict is happening, and git will leave a note for you of the content it is having trouble with. The note will look similar to what happens in the gitref guide here, under "merge conflicts".
  
 
http://gitref.org/branching/#merge
 
http://gitref.org/branching/#merge
Line 307: Line 377:
 
http://git-scm.com/book/en/Git-Branching-Rebasing
 
http://git-scm.com/book/en/Git-Branching-Rebasing
  
Note that unlike Linus and github, in this guide we aren't thinking about your github fork repo as public -- we prefer to think of it as your personal private IDE/tool as I described earlier. If other wesnoth devs are cloning it or pulling your topic branches, we assume you will work it out with them. With that caveat the philosophies expressed on github and by Linus should generally apply to the wesnoth project as well.
+
That's all for this guide, have fun hacking on wesnoth!
 +
 
 +
== Addendum ==
 +
The following section contains commands with the -f / --force options. Those are usually used to rewrite history, which is fine for your own fork but disallowed for upstream. Do not use them on the main repository.
 +
 
 +
 
 +
If a command goes awry, you can go to "git reflog", find the point just before you typed it, and reset --hard to that commit, and it should be like the bad thing never happened
 +
 +
revert every file in the repo to look like it did at the HEAD commit (last successful sync)
 +
        git reset --hard HEAD
 +
 
 +
get a clean slate by syncing with upstream
 +
        git fetch upstream
 +
        git checkout -B master upstream/master
 +
        git push --force origin master
 +
if this errors, try again after
 +
        git reset --hard HEAD
 +
 
 +
 +
undo n commits, split/squash and apply again
 +
        git reset HEAD~n
 +
        git add -p
 +
        git commit
 +
        git push -f
 +
 
 +
You can also push from one ‘ref’ to a different one:
 +
        $ git push <remote> <from>:<to>
 +
 
 +
completely overwrite branch b with a (from a to b)
 +
        git push -f origin a:b
 +
 
 +
 
 +
 
 +
 
 +
before any commit:
 +
 
 +
* if the diff shows an invisible change in the first line - stop! Fix the file encoding back to UTF8 without BOM.
 +
 +
* don't forget the changelog (and, for visible changes, the players_changelog)
 +
 +
* try to keep the summary at 50 characters or less, maximum 72
 +
 
 +
== See Also ==
 +
 
 +
* [[Project#Developers]]
  
That's all for this guide, have fun hacking on wesnoth!
+
[[Category:Development]]

Latest revision as of 06:25, 18 April 2023

Foreword

(An essay by, mainly, iceiceice.)

This page is intended for new Battle for Wesnoth developers and contributors who have never used Git before. I'm hoping that it will be written mainly by newbies like myself who have just figured out enough about Git to use it successfully, and therefore not include a bunch of information superfluous for that purpose. At the time that I am writing this, I have managed to write about eight GitHub pull requests and get them merged into Wesnoth, and I hope you will be able to do the same after reading this.

What is Git?

The most effective "absolute beginner" introduction to Git that I found when I started is here: gitref.org. - (Note: this gitref.org link seems to have died. Another resource, that is only slightly less introductory-level, is Pro-Git Book. Don't worry about reading the whole thing though.)

I suggest that you read this through, beginning to end, navigating through the pages by using the red arrows at the bottom of each page. You can skip the part about "stashing", but you should carefully read and understand the parts about:

  • cloning a repository;
  • checking out a branch, creating a new branch, and merging branches;
  • adding and committing changes;
  • viewing diffs to make sure you understand exactly what you are committing;
  • resetting your repository state in case you make a mistake and need to undo some steps, and using "git log" and "git reflog" to assist with this; and
  • moving content between local and remote repositories, with "push" and "pull".

Setting up our workflow for Wesnoth in Git

It is common when reading about Git to see the terms "upstream" and "downstream". This is often confusing at the beginning -- after all, information is usually flowing in both directions; sometimes, you are getting code from the Wesnoth repository, and sometimes you are sending code to it. Which way is up and which is down?

Analogously to a river, downstream is the direction toward which most information flows. Any user that simply wants to build Wesnoth from source, and not make any changes to it, will generally do so by cloning or pulling from <https://github.com/wesnoth/wesnoth>. So wesnoth/wesnoth (the main Wesnoth repository) is the upstream repository, and the users are downstream.

If you are a developer, you will fork the upstream repository and edit the source code, and -- hopefully, with work, eventually -- get your edits pulled into the upstream repository. That is the development cycle, and contributing changes back to upstream is what makes you a developer.

In this guide, we are going to assume that you will use GitHub in addition to the official Git command-line program, because GitHub has many useful tools to offer and makes some things nicely easy, especially when comparing a topic branch to the master branch and seeing diffs in a somewhat more advanced interface, and when making pull requests. Technically, this means that you will have two repositories -- your local repository on your computer, and a fork of the wesnoth/wesnoth repository, on GitHub.

The first step of becoming a Wesnoth developer is to set up these two repositories. Follow the instructions in GitHub's "Fork A Repo" help article, which may be summarized as follows:

  1. Fork the wesnoth/wesnoth repository, using the GitHub website.

  2. Clone the fork onto your computer, with the command "git clone".

  3. Configure remotes (remote repositories that are essentially bookmarked).

    GitHub's Fork A Repo article describes how to set the wesnoth/wesnoth repository as a remote with the name "upstream". Because you cloned from your fork, the fork is automatically set as a remote with the name "origin". You can see your remotes with the command "git remote -v".

    You might find this naming scheme confusing, given that the name "origin" might also aptly describe the wesnoth/wesnoth repository. So it might be good to rename that remote to "fork" (with the command "git remote rename origin fork"). But if you do that, note that much of the GitHub help pages will assume the name "origin".

Additionally, we ask that you configure Git to use your full real name or a consistent alias, per the instructions in GitHub's "Setting your username in Git" help article, so that we can associate a concrete person with every commit merged into Wesnoth. Your GitHub profile should also display the same name.

The basic workflow

Here's the picture we now have, with 3 repositories:


   upstream


                    origin (fork)


    local


There are four basic steps in the development cycle, which are as follows:

  1. ensuring that your local repository is up-to-date with the upstream repository;
  2. creating a topic branch for your changes;
  3. committing your changes to the topic branch and pushing them to your fork; and
  4. filing a GitHub pull request, having that request granted, then finally cleaning up.

Keeping your local repository up-to-date

In this step, you will make sure your local master branch is up-to-date with the most recent development changes before beginning work.

First make sure you are on your master branch:

 git checkout master
 git status

Now, pull the upstream master branch to your local repository:


   upstream
      |
      |
      |             origin (fork)
      |          
      v      
    local


 git pull upstream master

At this point your local master is up to date. While you are at it, you might as well update your fork as well:


   upstream
        
       
               .--> origin (fork)
              /  
             /    
    local --'


 git push origin master

At this point, the master branch should be the same in all three repositories.

Creating a topic branch

In this guide, we will never make any changes directly on the master branch, and will only make code changes on a topic branch, so regardless of which repository you look at, the master branch should be the same, and Git will be comparing your work against the upstream master branch.

Now you are ready to create a new topic branch, which will hold your work for this patch. We are on the master branch, as running "git status" will confirm. Create your new topic branch -- named, in this example, "new-feature" -- by running the following command:

 git checkout -b new-feature

As shown in the gitref.org guide, you could also have done this with the following commands:

 git branch new-feature
 git checkout new-feature

Given that you were on the master branch when you created the new-feature topic branch, the new-feature branch will branch off from the tip of your current master branch, which is best, to avoid conflicts when the new-feature branch is eventually merged upstream.

Committing your changes and pushing to your fork

Now, you will make your changes to Wesnoth, test them, and commit them to your local repository, with the commands "git add" and "git commit", as shown in the gitref.org guide.

Committing guidelines

  1. Write good commit messages

    A commit message should fully explain what changes were made in the commit, and why those changes were made. A good commit message is structured like an email message.

    The first line, which summarizes the commit, has special importance -- to quote the DeveloperGuide page, This is the first (and often the only) line someone using browsing tools will see. It should ideally be at most 80 characters (TODO: inconsistent with current policy), and is like the subject line of an email message. Commit message summary lines should be written in the imperative present tense, e.g., "Add field to some_class, with accessor methods", "Fix other_class's constructor", "Delete unnecessary #include", etc.

    After the summary line, there must be a blank line, after which comes the commit message body, which is analogous to the body of an email message.

    Optimally, a commit's message would be so clearly and comprehensively explanatory that it would answer any questions that anyone might have about the commit, now, the next day, or years into the future -- which is especially important because the author of the commit likely won't always be around to answer those questions.

    See also the DeveloperGuide page's "Commit messages" section and the official Git book's "Commit Guidelines" section.

  2. Make appropriately sized commits

    Each commit should correspond to a single logical step in your programming task. The commits constitute the project's development history; people will look at your commits to try to figure out how we got to where we are today, and may at some point need to undo the changes made in some of your commits to fix a future issue.

    If you find it difficult to write a succinct commit message that fully explain what changes were made in the commit, and why those changes were made, then the commit is insufficiently fine-grained -- too large and too complicated. Another guideline is, you should at least think about committing almost every time you compile (depending on your habits).

    On the other hand, you shouldn't make a commit for every line of code typed. If it would never make sense to revert the repository's code to its current state from a future state, you probably shouldn't commit. For example, if you define a field of an object, but it has no accessors and no way to be used in code, it would never make sense to revert to that time in the repository's history, so those changes should be saved together in a single commit. If you have a commit that merely adjusts whitespace in a file, that should be squashed in with another commit, according to current (2015-07-25) policy.

    It is better to commit more often than less often, because, before you submit your changes to upstream, you will have the opportunity to edit your commit history, and it is much easier to squash commits together than to split up an overly-large commit.

    See also the DeveloperGuide page's "Commits" section.

  3. Keep the change-logs, release notes, and credits up-to-date

    After committing your changes, add an entry in the changelog document, and commit it with the message "Update changelog".

    We also have a players_changelog document, which should contain only changes that most players are likely to notice, and be less technical than the main, technical changelog.

    There is also a RELEASE_NOTES document, which basically is copied into the next Wesnoth release's release announcement post in the forums, so if there is a new feature or bug fix that should be advertised, add that to RELEASE_NOTES.

    If this is your first commit, the development team will ask you to add an appropriate note about yourself in the credits document, data/about.cfg.

    See also the DeveloperGuide page's "Changelogs and release notes" section.

Finally, when your branch is ready, push it to your fork.


   upstream
       
       
               .--> origin (fork)
              /
             /
    local --'


 git push origin new-feature


Now, if you navigate to your fork's page on GitHub, you should see a message such as "recently pushed branch new-feature (3 minutes ago)". Click "Compare" to compare it against the master branch. You should now see a list of the commits you made on the new-feature branch, and color-highlighted diffs of the files you changed. You can click through the commits in order to see each of the changes you made and understand how your project progressed -- this is exactly what the development team will do when they review your pull request.

In the GitHub interface, you can always see a list of branches by clicking on "branches" in the line near the middle of the page that looks like the following:

10,000+ commits     27 branches     248 releases     85 contributors

You can also look at a specific branch using the "Branch" menu below that line, and you can compare branches using the green "Compare" button to the left of the "Branch" menu.

Cleaning up your branch

Before filing your pull request, if, looking at the diffs and the history (the list of commits) for your topic branch, you saw anything confusing or not quite right, now is your opportunity to change it.

From the gitref.org guide, you should know how to add more commits, and you should know that you can use "reset" to go back in time and redo things.

"reset" is especially good if your last commit was too big and you want to break it up into a series of smaller commits -- the command "git reset "@~"" will leave your working directory the same, but jump back in time one commit and unstage all of those changes, so you can add a smaller subset of the files, commit those, then add the others, etc.

More generally, you can use "git reflog" to see how to reset back over any number of commits or other Git operations.

You can also use "git commit --amend" to edit the commit message of the most recent commit.

There are several more advanced cleanup features available with Git, but we'll defer any further discussion of them.

Every time you make changes, you should to push them to your fork to keep it up-to-date. If you rewrote your commit history, then the push would be rejected, because the commit histories won't match. Therefore, to push a rewritten history, you must force push, which will -- this is a potentially dangerous operation! -- discard the old commit history and replace it with the new history:


   upstream
         
        
               .--> origin (fork)
              /
             /
    local --'


 git push --force origin new-feature

You can and should do this throughout your development if you want to look at the diffs of your changes as you work -- after all, the Wesnoth code-base is vast and it is easy to forget exactly where you are in your project. You can and should keep cleaning up in this way until you are satisfied with the result. Doing this, your GitHub fork ends up being rather like a personal Web-based IDE plug-in or extension to help you with development.

Filing a pull request

When you finish working on your feature or bug fix, select your topic branch on your fork (with the "Branch" menu), and click the green button to file a pull request. By default, the target of the pull request -- the repository and branch into which it is requesting that your changes be pulled -- will be "wesnoth:master", the master branch of the main Wesnoth repository.

The message that you enter for the pull request should become the commit message of the merge operation performed in the upstream repository if the pull request is granted. (Note: if you are granting someone's PR through the GitHub Web interface, please ensure that this happens.)

If you fixed a bug, you should say, e.g., "Fixed bug #5678" in the pull-request message, which should automatically link to our Gna! bug tracker.

If you want more information, you could read GitHub's "Creating a pull request" help article.


   upstream <-.
               \
                \
                 '- origin (fork)
 
 
    local     


You should then see your pull request in the main Wesnoth repository's "pull requests" page.

At the time of writing this, we also have configured Travis CI, a continuous integration system, to automatically try to compile Wesnoth with your changes, to make sure that it still works before your pull request is granted. However, it is not mandatory for the Travis build to complete successfully for the pull request to be granted. Additionally, you may still make changes to your pull request by pushing more changes to your topic branch in your fork, after which Travis should try to compile the updated topic branch. You can even still force push to erase the content of the topic branch (on which the pull request is based) and replace it with new content -- however, you shouldn't file a pull request for a topic branch until you are ready for it to be merged.

Now, wait for a member of the development team to review your pull request. You can often find someone on the development IRC channel, #wesnoth-dev. Additionally, developers may comment directly on your pull request with questions, so you should check it periodically.

Cleaning up at the end

Congratulations, a developer granted your pull request! Now it's time to clean up and update your local repository.

On the GitHub page for your pull request, you should now see a button with the option "delete this branch". Since the content of new-feature is now merged into master, this branch no longer needs to exist as you won't work on it anymore, and future work will go onto a different branch. So you should delete it, which will delete the branch on *origin*, your github-associated repo.

Since master has now changed and we want master to be synced up, you should now do step 1 again:


   upstream
      |
      |
      |             origin (fork)
      |          
      v      
    local


 git checkout master
 git pull upstream master

At this point git will understand that new-feature has been merged into master, so when you ask to delete this branch from local as well, it will do it:

 git branch -d new-feature

If you do these steps out of order, i.e. try to delete the branch before before syncing master, it will make a warning "Are you sure? You have unmerged changes on this branch..." etc.

Finally, sync master on the origin as well:


   upstream
         
         
                    origin (fork)
              /-->  
           --/    
    local     


 git push origin master

That's the end of the development cycle, and now you can begin on your next patch. Note that throughout the cycle, information only flowed counter-clockwise:


   upstream
      |     <--\
      |         \--
      |             origin (fork)
      |       /-->  
      v    --/    
    local


If for some reason you find yourself doing something where information is flowing the other way, that is a good sign that something is going wrong, at least as far as this guide is concerned! (The exception is the steps in which we set up our repos -- don't overthink this.)


Advanced / Alternative workflows

You can use pull requests as described above whether you have commit access or not -- if you have commit access, then you can click to accept your own pull requests.

However, if you have commit access, you may prefer not to use github / pull requests at all, and instead to push directly to wesnoth:master. You can still use topic branches to hold your work, and merge them to your local master using git merge, then use 'git push upstream master' to push them to the main wesnoth repo.

If you are lazy, you might even skip making a topic branch and just commit to local master, although IMO this can make things more confusing if you get a merge conflict later.

Either way, afaik the vast majority of wesnoth developers prefer to push directly to wesnoth:master.

An extreme option appropriate for small changes is to skip the local repo as well, and simply commit changes directly from the github web interface. This might be appropriate for example if you forgot to commit a changelog entry in an earlier patch, or are simply changing some numbers used as parameters somewhere. Obviously if you change source code this way, you should make certain that everything still compiles immediately after! (Note: Don't actually do that.)

The ultimate cleanup power tool: git rebase

Using things in the gitref guide, you can see how to use reset to undo mistakes you made by backing up and trying again, and for small commits that is probably fine. However, if you have a large and complicated series of changes, the better way to do this is using git rebase. Using git rebase in interactive mode, you can rewrite history by

  • reordering your commits
  • discarding unwanted commits
  • squashing commits together
  • editing the content changes represented by an individual commit
  • editing the commit message

For our purposes, we assume you are working on a topic branch, and then you will only need to use the form

 git rebase -i master

You can see some documentation for using this command here: https://help.github.com/articles/interactive-rebase

Using 'git rebase', you can make your commit history very *clean* -- instead of the history saying "I tried a bunch of things, undid some of them, tried some other things, and found something I liked", your commit history can basically be "I took the simplest and most logical route to the solution that was finally the best". When you review your commits, instead of feeling like a nasty blood-and-elbow-grease engineering project, it should feel like folding a perfect origami crane, to the extent possible ;)

But to reiterate, this has a purpose.

  1. Other devs need to be able to understand the history.
  2. It should be possible to jump back in time by checking out one of your commits and find ourselves in a sensible state when we do so.
  3. If something breaks or some feature become incompatible, it should be possible to roll back only a piece of the implementation of your feature without removing the whole thing.

If you want to use rebase to cleanup your history, it is important to do it *before* it is pulled into wesnoth, as once that happens it is basically no longer possible. If we rewrite history in the main wesnoth repo, then afterwards whenever any dev tries to 'git pull upstream master', their git will give them strange merge errors, stemming from the incompatible history, and then they will become nervous, jump on irc and exclaim some variation of "omg wtf bbq". So except in extreme circumstances, we will never rebase the main wesnoth master, and for the same reason, we will never use "git push --force upstream master". That's why if you want to do these things, it is important to do them on your *fork* before it makes it onto master.

If you feel like it, you might read this, which is a historical email between early users / developers of git, in which Linus Torvalds explains about shared history of repos and when it is appropriate to use git rebase.

http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html

When you become comfortable with git rebase -i, it will greatly improve your workflow, as you will know that you can easily review and revise commit messages later, and easily reorder and squash commits. Typically I will now commit every single time I compile, and even if it is a commit which e.g. fixes a compile time error, or for some other reason I know it will be squashed in somewhere else, I give it the commit message "fixup", as a note to myself to mark it thusly in the first git rebase pass. (Actually, while writing this I have just learned about the --autosquash feature of git rebase which takes this idea further, good stuff there.)

Note that unlike Linus and github, in this guide we aren't thinking about your github fork repo as public -- we prefer to think of it as your personal private IDE/tool as I described earlier. If other wesnoth devs are cloning it or pulling your topic branches, we assume you will work it out with them. With that caveat the philosophies expressed on github and by Linus should generally apply to the wesnoth project as well.

Solving merge conflicts with git rebase

Github version: https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request

Suppose that you modify a file in the same place as someone else, and their change gets merged before yours. If git can't figure out how to merge, then someone will have to fix it. This could be done by whoever is merging the new content into master, but if you don't have commit access then that person is not you. One way that *you* could fix it is to resync your master to get the new changes locally, and then rebase your topic branch so that your changes are applied *after* the most recent changes.

 git rebase master

To understand what is happening, we should understand a bit more about how git works. Intuitively, git is all about "snapshots" of the project content. Every commit represents a snapshot, and you can look at this snapshot by checking out the commit. However git does not store each snapshot separately -- instead git defines the snapshots in terms of one another, and stores only the diffs. When you rebase your topic branch onto master, this will also update the "point of departure" where your branch was created, so that in the history, it will depart from the current head of master with the most recent changes. (Obviously, if you don't sync master until you are done working, then this subtlety to 'git rebase' is irrelevant.)

If merging your branch with the current master would create a conflict, then when git rebase gets to the commit that creates the conflict, it will get confused when it tries to apply that diff, stop, tell you about the problem, and ask you to resolve it -- you can open up the offending file and go to the point where the conflict is happening, and git will leave a note for you of the content it is having trouble with. The note will look similar to what happens in the gitref guide here, under "merge conflicts".

http://gitref.org/branching/#merge

You just have to remove the note, make the file look like it should to make everything compatible, and type

 git add .
 git rebase --continue

Then the rebasing process will continue, and at the end your branch should be compatible with master and ready to be merged in.

You can read more about this aspect of git rebase here if you like, although most likely you won't actually need more than we just talked about for wesnoth development.

http://git-scm.com/book/en/Git-Branching-Rebasing

That's all for this guide, have fun hacking on wesnoth!

Addendum

The following section contains commands with the -f / --force options. Those are usually used to rewrite history, which is fine for your own fork but disallowed for upstream. Do not use them on the main repository.


If a command goes awry, you can go to "git reflog", find the point just before you typed it, and reset --hard to that commit, and it should be like the bad thing never happened

revert every file in the repo to look like it did at the HEAD commit (last successful sync)

       git reset --hard HEAD

get a clean slate by syncing with upstream

       git fetch upstream
       git checkout -B master upstream/master
       git push --force origin master

if this errors, try again after

       git reset --hard HEAD


undo n commits, split/squash and apply again

       git reset HEAD~n
       git add -p
       git commit
       git push -f

You can also push from one ‘ref’ to a different one:

       $ git push <remote> <from>:<to>

completely overwrite branch b with a (from a to b)

       git push -f origin a:b



before any commit:

  • if the diff shows an invisible change in the first line - stop! Fix the file encoding back to UTF8 without BOM.
  • don't forget the changelog (and, for visible changes, the players_changelog)
  • try to keep the summary at 50 characters or less, maximum 72

See Also

This page was last edited on 18 April 2023, at 06:25.