developing_in_github.md (13787B)
1 # Developing in GitHub 2 3 This document describes the development steps related to handling the git 4 repository. 5 6 If you are new to GitHub, there's a nice [quickstart 7 guide](https://docs.github.com/en/github/getting-started-with-github/quickstart) 8 on GitHub explaining the basics. 9 10 ## Initial setup 11 12 You need to perform this set up at least once if you haven't use GitHub before. 13 Read through the quickstart guide [Set up 14 Git](https://docs.github.com/en/github/getting-started-with-github/set-up-git) 15 page to get your git up and running. You will need to Fork a repository next. 16 After that "Life of a Pull Request" describes the common everyday workflows. 17 18 ### Configure your SSH access 19 20 The easiest way to configure access to your Github repository is to use SSH 21 keys. For that you need an SSH private and public key, ideally a strong one. You 22 can use different keys for different sites if you want. In this example, we will 23 create one for using in GitHub only. 24 25 Create the `~/.ssh/id_rsa_github` file executing the following. (Here and 26 elsewhere, {{X}} are placeholders for your email/username) 27 28 ```bash 29 ssh-keygen -t rsa -b 4096 -C "{{EMAIL}}" -f ~/.ssh/id_rsa_github 30 ``` 31 32 Go to your [SSH and GPG keys](https://github.com/settings/keys) settings and 33 paste the contents of your *public key* (the one ending in `.pub`), that would 34 be the output of this command: 35 36 ```bash 37 cat ~/.ssh/id_rsa_github.pub 38 ``` 39 40 To use a specific key when SSHing to the github.com domain, you can add this 41 snippet of config to your .ssh/config file executing the following. 42 43 ```bash 44 cat >> ~/.ssh/config <<EOF 45 46 Host github.com 47 Hostname github.com 48 IdentityFile ~/.ssh/id_rsa_github 49 IdentitiesOnly yes 50 EOF 51 ``` 52 53 The `IdentitiesOnly yes` part forces to only use the provided IdentityFile when 54 talking to GitHub. 55 56 ### Fork your private copy 57 58 The JPEG XL code is located in [this repo](https://github.com/libjxl/libjxl). 59 60 The normal developer workflow in GitHub involves creating your own fork of a 61 repository and uploading your own changes there. From your own copy you can 62 request merges *to* the upstream repository directly, there's no need to create 63 a branch in the upstream repository. 64 65 [Fork the 66 repository](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) 67 in GitHub to create your own copy of the repository in GitHub. You can then 68 propose to include changes in the main repository via a Pull Request. 69 70 Once you are done you should have your repository at 71 72 https://<!-- not a link -->github.com<!-- not a link -->/*{{USERNAME}}*/libjxl 73 74 where {{USERNAME}} denotes your GitHub username. 75 76 ### Checkout the JPEG XL code from GitHub 77 78 To get the source code on your computer you need to "clone" it. There are two 79 repositories at play here, the upstream repository (`libjxl/lbjxl`) and your 80 fork (`{{USERNAME}}/libjxl`). You will be normally fetching new changes from 81 the upstream repository and push changes to your fork. Getting your changes from 82 your fork to the upstream repository is done through the Web interface, via Pull 83 Requests. 84 85 The [Fork a 86 repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) 87 goes in great detail, but uses the git remote names `upstream` for the shared 88 upstream repository and `origin` for your work. This guide proposes an 89 alternative naming scheme, used in the examples below. 90 91 In this guide `origin` is the upstream shared repository and `myfork` is your 92 fork. You can use any other name for your fork if you want. Use the following 93 commands to set things up, replacing `{{USERNAME}}` with your GitHub username: 94 95 ```bash 96 git clone git https://github.com/libjxl/libjxl --recursive 97 cd libjxl 98 git remote set-url --push origin git@github.com:{{USERNAME}}/libjxl.git 99 git remote add myfork git@github.com:{{USERNAME}}/libjxl.git 100 git remote -vv 101 ``` 102 103 These commands did three things: 104 105 * Created the repository with `origin` as the upstream remote, 106 * Changed the "push" URL to point to your fork, and 107 * Create a new remote pointing to your fork. 108 109 The last step is optional. Since the "fetch" URL of `origin` points to the 110 shared repository and the "push" URL points to your fork, fetching from `origin` 111 always gets the latest changes from the upstream repository regardless of the 112 contents of your fork. 113 114 Having a second origin called `myfork` is only useful if you need to download 115 pending changes from your fork from a different computer. For example, if you 116 work on multiple computers, each one with this setup, you can push to your 117 fork from one, and then fetch from `myfork` from another computer to get those. 118 119 # Life of a Pull Request 120 121 The general [GitHub flow 122 guide](https://docs.github.com/en/github/getting-started-with-github/github-flow) 123 applies to sending Pull Requests to this project. 124 125 All the commands here assume you are in a git checkout as setup here. 126 127 ### Sync to the latest version 128 129 ```bash 130 git fetch origin 131 ``` 132 133 The last upstream version is now on `origin/main` and none of your local 134 branches have been modified by this command. 135 136 ### Start a new branch 137 138 To start a new change you need a local branch. Each branch will represent a list 139 of individual commits which can then be requested to be merged as a single merge 140 request. So in general one branch is one code review, but each branch can have 141 multiple individual commits in it. 142 143 ```bash 144 git checkout origin/main -b mybranch 145 ``` 146 147 This will create a new branch `mybranch` tracking `origin/main`. A branch can 148 track any remove or local branch, which is used by some tools. Running `git 149 branch -vv` will show all the branches you have have, what are they tracking and 150 how many commits are ahead or behind. If you create a branch without tracking 151 any other, you can add or change the tracking branch of the current branch 152 running `git branch --set-upstream-to=...`. 153 154 ### Add changes to your branch 155 156 Follow any of the many online tutorials, for example 157 [The basics](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) 158 chapter from the https://git-scm.com/doc website is a good starting guide. 159 Create, change or delete files and do a git commit with a message. 160 161 The commit message is required. A commit message should follow the 50/72 rule: 162 163 * First line is 50 characters or less. 164 * Then a blank line. 165 * Remaining text should be wrapped at 72 characters. 166 167 The first line should identify your commit, since that's what most tools will 168 show to the user. First lines like "Some fixes" are not useful. Explain what the 169 commit contains and why. 170 171 We follow the [Google C++ Coding 172 Style](https://google.github.io/styleguide/cppguide.html). A 173 [clang-format](https://clang.llvm.org/docs/ClangFormat.html) configuration 174 file is available to automatically format your code, you can invoke it with 175 the `./ci.sh lint` helper tool. 176 177 Read the [CONTRIBUTING.md](../CONTRIBUTING.md) file for more information about 178 contributing to libjxl. 179 180 ### Upload your changes for review 181 182 The first step is a local review of your changes to see what will you be sending 183 for review. `gitg` is a nice Gtk UI for reviewing your local changes, or `tig` 184 for similar ncurses console-based interface. Otherwise, from the terminal you 185 can run: 186 187 ```bash 188 git branch -vv 189 ``` 190 191 To show the current status of your local branches. In particular, since your 192 branch is tracking origin/main (as seen in the output) git will tell you that 193 you are one commit ahead of the tracking branch. 194 195 ``` 196 * mybranch e74ae1a [origin/main: ahead 1] Improved decoding speed by 40% 197 ``` 198 199 It is a good idea before uploading to sync again with upstream (`git fetch 200 origin`) and then run `git branch -vv` to check whether there are new changes 201 upstream. If that is the case, you will see a "behind" flag in the output: 202 203 ``` 204 * mybranch e74ae1a [origin/main: ahead 1, behind 2] Improved decoding speed by 40% 205 ``` 206 207 To sync your changes on top of the latest changes in upstream you need to 208 rebase: 209 210 ```bash 211 git rebase 212 ``` 213 214 This will by default rebase your current branch changes on top of the tracking 215 branch. In this case, this will try to apply the current commit on top of the 216 latest origin/main (which has 2 more commits than the ones we have in our 217 branch) and your branch will now include that. There could be conflicts that you 218 have to deal with. A shortcut to do both fetch and rebase is to run `git pull 219 -r`, where the `-r` stands for "rebase" and will rebase the local commits on top 220 of the remote ones. 221 222 Before uploading a patch, make sure your patch conforms to the 223 [contributing guidelines](../CONTRIBUTING.md) and it 224 [builds and passes tests](building_and_testing.md). 225 226 Once you are ready to send your branch for review, upload it to *your* fork: 227 228 ```bash 229 git push origin mybranch 230 ``` 231 232 This will push your local branch "mybranch" to a remote in your fork called 233 "mybranch". The name can be anything, but keep in mind that it is public. A link 234 to the URL to create a pull request will be displayed. 235 236 ``` 237 Enumerating objects: 627, done. 238 Counting objects: 100% (627/627), done. 239 Delta compression using up to 56 threads 240 Compressing objects: 100% (388/388), done. 241 Writing objects: 100% (389/389), 10.71 MiB | 8.34 MiB/s, done. 242 Total 389 (delta 236), reused 0 (delta 0) 243 emote: 244 remote: Create a pull request for 'mybranch' on GitHub by visiting: 245 remote: https://github.com/{{USERNAME}}/libjxl/pull/new/mybranch 246 remote: 247 To github.com:{{USERNAME}}/libjxl.git 248 * [new branch] mybranch -> mybranch 249 ``` 250 251 ### Updating submodules 252 253 The repository uses submodules for external library dependencies in 254 third_party. Each submodule points to a particular external commit of the 255 external repository by the hash code of that external commit. Just like 256 regular source code files, this hash code is part of the current branch and 257 jpeg xl commit you have checked out. 258 259 When changing branches or when doing `git rebase`, git will unfortunately 260 *not* automatically set those hashes to the ones of the branch or jpeg xl 261 commit you changed to nor set the source files of the third_party submodules 262 to the new state. That is, even though git will have updated the jpeg xl 263 source code files on your disk to the new ones, it will leave the submodule 264 hashes and the files in third_party in your workspace to the ones they were 265 before you changed branches. This will show up in a git diff because this 266 is seen as a change compared to the branch you switched to. The git diff shows 267 the difference in hash codes (as if you are changing to the old ones), it does 268 not show changes in files inside the third_party directory. 269 270 This mismatch can cause at least two problems: 271 272 *) the jpeg xl codebase may not compile due to third_party library version 273 mismatch if e.g. API changed or a submodule was added/removed. 274 275 *) when using `commit -a` your commit, which may be a technical change 276 unrelated to submodule changes, will unintentionally contain a change to the 277 submodules hash code, which is undesired unless you actually want to change 278 the version of third_party libraries. 279 280 To resolve this, the submodules must be updated manually with 281 the following command after those actions (at least when the submodules 282 changed): 283 284 ``` 285 git submodule update --init --recursive 286 ``` 287 288 Here, the init flag ensures new modules get added when encessary and the 289 recursive flag is required for the submodules depending on other submodules. 290 291 If you checkout a different branch, you can spot that submodules changed 292 when it shows a message similar to this: 293 294 ``` 295 M third_party/brotli 296 M third_party/lcms 297 ``` 298 299 If you do a rebase you may end up in a harder to solve situation, where 300 `git submodule update --init --recursive` itself fails with errors such as: 301 302 ``` 303 Unable to checkout '35ef5c554d888bef217d449346067de05e269b30' in submodule path 'third_party/brotli' 304 ``` 305 306 In that case, you can use the force flag: 307 308 ``` 309 git submodule update --init --recursive --force 310 ``` 311 312 ### Iterating changes in your pull request 313 314 To address reviewer changes you need to amend the local changes in your branch 315 first. Make the changes you need in your commit locally by running `git commit 316 --amend file1 file2 file3 ...` or `git commit --amend -a` to amend all the 317 changes from all the staged files. 318 319 Once you have the new version of the "mybranch" branch to re-upload, you need to 320 force push it to the same branch in your fork. Since you are pushing a different 321 version of the same commit (as opposed to another commit on top of the existing 322 ones), you need to force the operation to replace the old version. 323 324 ```bash 325 git push origin mybranch --force 326 ``` 327 328 The pull request should now be updated with the new changes. 329 330 ### Merging your changes 331 332 We use "rebase" as a merge policy, which means that there a no "merge" commits 333 (commits with more than one parent) but instead only a linear history of 334 changes. 335 336 It is possible that other changes where added to the main branch since the last 337 time you rebased your changes. These changes could create a conflict with your 338 Pull Request, if so you need to `git fetch`, `git rebase` and push again your 339 changes which need to go through the continuous integration workflow again to 340 verify that all the tests pass again after including the latest changes. 341 342 ### Trying locally a pending Pull Request 343 344 If you want to review in your computer a pending pull request proposed by 345 another user you can fetch the pull request commit with the following command, 346 replacing `NNNN` with the pull request number: 347 348 ```bash 349 git fetch origin refs/pull/NNNN/head 350 git checkout FETCH_HEAD 351 ``` 352 353 The first command will add to your local git repository the remote commit for 354 the pending pull request and store a temporary reference called `FETCH_HEAD`. 355 The second command then checks out that reference. From this point you can 356 review the files in your computer, create a local branch for this FETCH_HEAD or 357 build on top of it.