21 | | '''T.B.D.:''' MacPorts ports contributors should be encouraged to fork the original MacPorts {{{git}}} repository and introduce changes only in their forked repos. Contributions can then be "filed" to the original repo using GitHub's pull requests (PR). These PRs allow for user-friendly reviewing, commenting and in the future possibly also immediate issuing of CI builds. |
22 | | |
23 | | |
24 | | == Common `git` tasks while working with ports == |
25 | | |
26 | | Then |
27 | | {{{ |
28 | | svn checkout https://svn.macports.org/repository/macports/trunk/dports |
29 | | }}} |
30 | | becomes |
31 | | {{{ |
32 | | git clone git@github.com:macports/ports.git |
33 | | }}} |
34 | | When you clone you will get the entire history of the ports tree, with the latest version being checked out in the filesystem. |
35 | | After you make a change, you can run {{{ git status }}} |
36 | | and get something like this. |
37 | | {{{ |
38 | | On branch master |
39 | | Your branch is up-to-date with 'origin/master'. |
40 | | Changes not staged for commit: |
41 | | (use "git add <file>..." to update what will be committed) |
42 | | (use "git checkout -- <file>..." to discard changes in working directory) |
43 | | |
44 | | modified: aqua/iTerm2/Portfile |
45 | | |
46 | | no changes added to commit (use "git add" and/or "git commit -a") |
47 | | }}} |
48 | | What this tells me, is that I've changed a Portfile, but not done anything. |
49 | | After that, you can add the files that you want to add to your commit using {{{git add aqua/iTerm2/Portfile}}}. |
50 | | Now, {{{git status}}} will look like: |
51 | | {{{ |
52 | | On branch master |
53 | | Your branch is up-to-date with 'origin/master'. |
54 | | Changes to be committed: |
55 | | (use "git reset HEAD <file>..." to unstage) |
56 | | |
57 | | modified: aqua/iTerm2/Portfile |
58 | | }}} |
59 | | Then run {{{git commit}}} and everything is set. On your machine. To push to github you then have to run {{{git push}}}. |
60 | | |
61 | | |
62 | | == Common `git` tasks while working with MacPorts base == |
63 | | |
64 | | === Checking out a working copy === #clone |
65 | | The source code of MacPorts itself is no longer managed in the same repository as all ports. Contrary to Subversion, checking out a sub-directory of a repository is not possible with Git. In order to avoid that all port maintainers have to clone the complete history of MacPorts base as well, the Subversion repository has been split into multiple separate repositories. MacPorts base is now available using |
66 | | {{{ |
67 | | git clone git@github.com:macports/base.git # or |
68 | | git clone https://github.com/macports/base.git # if SSH does not work on your network |
69 | | }}} |
70 | | |
71 | | See the [#reposplit section on repository splitting during the export] to get an overview of where a path in the old Subversion history is now available in Git. |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | == Common `git` tasks & notes about MacPorts' Subversion export == |
| 20 | |
| 21 | |
| 22 | === Getting a working copy === |
| 23 | To start working with git, you need a copy of the sources. Follow the sections for [#cloneports getting the ports tree] or [#clone MacPorts base], depending on where you want to make changes, then continue with the next section. |
| 24 | |
104 | | === Merge a single change from master into a release branch === #cherrypick |
105 | | The equivalent to Subversion's `svn merge -c <revision> .` is `git cherry-pick`. Use `git cherry-pick` to apply a single change from master to a release branch. To do this, look up the commit ID of the commit you want to pick: |
106 | | {{{ |
107 | | git log |
108 | | # copy the commit ID |
109 | | }}} |
110 | | Switch to the target branch of the cherry pick: |
111 | | {{{ |
112 | | git checkout release_2_3 |
113 | | }}} |
114 | | Cherry-pick the commit. It is good practice to pass `-x` to `git cherry-pick`, which will automatically add a "Cherry picked from commit <commmitID>" line to the commit message of your cherry pick. You will have the option to modify the commit message, e.g. to describe why the backport was necessary. |
115 | | {{{ |
116 | | git cherry-pick -x <commitID> |
117 | | }}} |
118 | | Finally, push the new commit using |
119 | | {{{ |
120 | | git push origin <branchname> |
121 | | }}} |
122 | | |
123 | | == Common `git` tasks & notes about MacPorts' Subversion export == |
124 | | === Fetching the latest changes === #updating |
125 | | Git's equivalent to `svn update` is a little more complicated due to Git's distributed nature. Most of the complexity is not visible if you do not have commits in your working copy that have not been pushed yet. If both the local and the remote repository have changes (git calls them "diverged"), you will run into one of Git's core principles: Every commit has (at least) one parent commit, i.e. the commit history forms a directed acyclic graph. |
126 | | |
127 | | ==== Background knowledge ==== |
128 | | |
129 | | A picture is worth a thousand words: |
130 | | {{{ |
131 | | A --- B --- C ---- R1 ---- R2 ---- R3 <= origin/master |
132 | | \ |
133 | | +--- L1 ---- L2 <= master |
134 | | }}} |
135 | | A, B and C are commits that are both in your local and in the remote repository. R1-3 are commits that have been pushed into the remote repository "origin"'s master branch while you were working. L1 and L2 are commits you prepared locally on your master branch. Git offers two different ways to bring R1-3 into your local branch: |
136 | | |
137 | | ===== Merging ===== |
138 | | A merge commit, created by `git merge`, is a commit that has multiple parents. If no conflict occurs, merge commits do not usually have a diff attached (i.e. they do not modify files). On conflict, merge commits contain the diff that resolves the conflict. In pictures: |
139 | | {{{ |
140 | | A --- B --- C ---- R1 ---- R2 ---- R3 <= origin/master |
141 | | \ \ |
142 | | +--- L1 ---- L2 ------ M <= master |
143 | | }}} |
144 | | The new commit M is the merge commit and can be pushed back to origin. This preserves the information that work was done in parallel, but unfortunately tends to mess up the history graph. See the [raw-attachment:commit-history-with-excessive-merging.png attached screenshot] of a commit history that always merges. To avoid this, you can instead rebase your changes. |
145 | | |
146 | | ===== Rebasing ===== |
147 | | Rebasing commits rewrites their parent commit IDs and avoids the need for a merge commit. Running `git rebase origin/master` will take all commits in your local working copy that are not yet pushed and attach them after the end of `origin/master`, which yields this picture: |
148 | | {{{ |
149 | | A --- B --- C ---- R1 ---- R2 ---- R3 <= origin/master |
150 | | \ |
151 | | L1' ---- L2' <= master |
152 | | |
153 | | }}} |
154 | | Note that L1 and L2 have been modified by this operation; their commit IDs changed because of that. This new state can be pushed back to origin without the need for a merge commit, and the history graph will stay linear. '''We recommend that all developers rebase their changes rather than merge when conflicts occur during pushing.''' |
155 | | |
156 | | ==== Putting the background knowledge into production ==== |
157 | | First, get all new commits from the remote repository using `git fetch <remote-name>`, where `<remote-name>` identifies the repository from which you want to fetch and defaults to "origin": |
158 | | {{{ |
159 | | git fetch |
160 | | }}} |
161 | | Then, rebase your local changes (if any) on top of any new changes in the remote repository and fix any conflicts that occur: |
162 | | {{{ |
163 | | git rebase origin/master |
164 | | }}} |
165 | | |
166 | | Because these two operations are very common, Git offers a shorthand for them: |
167 | | {{{ |
168 | | git pull --rebase |
169 | | }}} |
170 | | |
171 | | '''Note:''' `git rebase` requires that you do not have uncommitted modifications in your working copy. If you have modifications, you can temporarily save them using `git stash` and restore them after the rebase using `git stash pop`. |
172 | | |
173 | | '''Warning:''' `git pull` without the `--rebase` flag is a shorthand for `git fetch && git merge origin/master`, which will automatically create a merge commit if it thinks that's necessary. |
174 | | |
175 | | If you do not want to remember passing `--rebase` to `git pull` every time you run it, you can set a couple of [https://git-scm.com/docs/git-config git-config(1)] options to make it the default: |
176 | | |
177 | | - Setting `pull.rebase` to `true` will change the default to always rebase when calling `git pull`. Note that this will also flatten any local merge commits you might have committed on purpose with `git merge`, which might be undesirable when merging development branches for MacPorts base. Consider using the `preserve` setting, which avoids this. |
178 | | - Rebasing can be enabled on a per-branch basis using the `branch.<name>.rebase` setting, which accepts the same values as `pull.rebase`. |
179 | | - You can make `branch.<name>.rebase true` the default for all branches that you clone by setting `branch.autoSetupRebase` to `always`. This allows you to change the setting back to a different value for specific branches but still keep the default to rebase. Note that this setting will not affect branches that you have already created. |
| 71 | |
| 72 | === Fetching the latest changes === #updating |
| 73 | Git's equivalent to `svn update` is a little more complicated due to Git's distributed nature. Most of the complexity is not visible if you do not have commits in your working copy that have not been pushed yet. If both the local and the remote repository have changes (git calls them "diverged"), you will run into one of Git's core principles: Every commit has (at least) one parent commit, i.e. the commit history forms a directed acyclic graph. |
| 74 | |
| 75 | ==== Background knowledge ==== |
| 76 | |
| 77 | A picture is worth a thousand words: |
| 78 | {{{ |
| 79 | A --- B --- C ---- R1 ---- R2 ---- R3 <= origin/master |
| 80 | \ |
| 81 | +--- L1 ---- L2 <= master |
| 82 | }}} |
| 83 | A, B and C are commits that are both in your local and in the remote repository. R1-3 are commits that have been pushed into the remote repository "origin"'s master branch while you were working. L1 and L2 are commits you prepared locally on your master branch. Git offers two different ways to bring R1-3 into your local branch: |
| 84 | |
| 85 | ===== Merging ===== |
| 86 | A merge commit, created by `git merge`, is a commit that has multiple parents. If no conflict occurs, merge commits do not usually have a diff attached (i.e. they do not modify files). On conflict, merge commits contain the diff that resolves the conflict. In pictures: |
| 87 | {{{ |
| 88 | A --- B --- C ---- R1 ---- R2 ---- R3 <= origin/master |
| 89 | \ \ |
| 90 | +--- L1 ---- L2 ------ M <= master |
| 91 | }}} |
| 92 | The new commit M is the merge commit and can be pushed back to origin. This preserves the information that work was done in parallel, but unfortunately tends to mess up the history graph. See the [raw-attachment:commit-history-with-excessive-merging.png attached screenshot] of a commit history that always merges. To avoid this, you can instead rebase your changes. |
| 93 | |
| 94 | ===== Rebasing ===== |
| 95 | Rebasing commits rewrites their parent commit IDs and avoids the need for a merge commit. Running `git rebase origin/master` will take all commits in your local working copy that are not yet pushed and attach them after the end of `origin/master`, which yields this picture: |
| 96 | {{{ |
| 97 | A --- B --- C ---- R1 ---- R2 ---- R3 <= origin/master |
| 98 | \ |
| 99 | L1' ---- L2' <= master |
| 100 | |
| 101 | }}} |
| 102 | Note that L1 and L2 have been modified by this operation; their commit IDs changed because of that. This new state can be pushed back to origin without the need for a merge commit, and the history graph will stay linear. '''We recommend that all developers rebase their changes rather than merge when conflicts occur during pushing.''' |
| 103 | |
| 104 | ==== Putting the background knowledge into production ==== |
| 105 | First, get all new commits from the remote repository using `git fetch <remote-name>`, where `<remote-name>` identifies the repository from which you want to fetch and defaults to "origin": |
| 106 | {{{ |
| 107 | git fetch |
| 108 | }}} |
| 109 | Then, rebase your local changes (if any) on top of any new changes in the remote repository and fix any conflicts that occur: |
| 110 | {{{ |
| 111 | git rebase origin/master |
| 112 | }}} |
| 113 | |
| 114 | Because these two operations are very common, Git offers a shorthand for them: |
| 115 | {{{ |
| 116 | git pull --rebase |
| 117 | }}} |
| 118 | |
| 119 | '''Note:''' `git rebase` requires that you do not have uncommitted modifications in your working copy. If you have modifications, you can temporarily save them using `git stash` and restore them after the rebase using `git stash pop`. |
| 120 | |
| 121 | '''Warning:''' `git pull` without the `--rebase` flag is a shorthand for `git fetch && git merge origin/master`, which will automatically create a merge commit if it thinks that's necessary. |
| 122 | |
| 123 | If you do not want to remember passing `--rebase` to `git pull` every time you run it, you can set a couple of [https://git-scm.com/docs/git-config git-config(1)] options to make it the default: |
| 124 | |
| 125 | - Setting `pull.rebase` to `true` will change the default to always rebase when calling `git pull`. Note that this will also flatten any local merge commits you might have committed on purpose with `git merge`, which might be undesirable when merging development branches for MacPorts base. Consider using the `preserve` setting, which avoids this. |
| 126 | - Rebasing can be enabled on a per-branch basis using the `branch.<name>.rebase` setting, which accepts the same values as `pull.rebase`. |
| 127 | - You can make `branch.<name>.rebase true` the default for all branches that you clone by setting `branch.autoSetupRebase` to `always`. This allows you to change the setting back to a different value for specific branches but still keep the default to rebase. Note that this setting will not affect branches that you have already created. |
| 128 | |
| 129 | |
| 168 | |
| 169 | |
| 170 | |
| 171 | == Common `git` tasks while working with ports == |
| 172 | |
| 173 | |
| 174 | === Checking out a working copy of the ports tree === #cloneports |
| 175 | To get a working copy of the MacPorts ports tree to start changing ports, clone of copy of the repository: |
| 176 | {{{ |
| 177 | git clone git@github.com:macports/ports.git # or |
| 178 | git clone https://github.com/macports/ports.git # if SSH does not work on your network |
| 179 | }}} |
| 180 | |
| 181 | This will give you the entire history of the ports tree, with the latest version being checked out in the filesystem. |
| 182 | |
| 183 | '''Note:''' If you intend to submit a pull request on GitHub to get your changes included in MacPorts, you may want to create a fork of the repository first. To do that, go to https://github.com/macports/ports/ and click the fork button at the top right. Then, run the command above, but use `<yourusername>/ports.git` instead of `macports/ports.git`. |
| 184 | |
| 185 | See the [#commit section on committing changes] to find out how to get your changes into the repository. |
| 186 | |
| 187 | |
| 188 | |
| 189 | |
| 190 | |
| 191 | == Common `git` tasks while working with MacPorts base == |
| 192 | |
| 193 | |
| 194 | === Checking out a working copy of MacPorts base === #clone |
| 195 | The source code of MacPorts itself is no longer managed in the same repository as all ports. Contrary to Subversion, checking out a sub-directory of a repository is not possible with Git. In order to avoid that all port maintainers have to clone the complete history of MacPorts base as well, the Subversion repository has been split into multiple separate repositories. MacPorts base is now available using |
| 196 | {{{ |
| 197 | git clone git@github.com:macports/base.git # or |
| 198 | git clone https://github.com/macports/base.git # if SSH does not work on your network |
| 199 | }}} |
| 200 | |
| 201 | See the [#reposplit section on repository splitting during the export] to get an overview of where a path in the old Subversion history is now available in Git. |
| 202 | |
| 203 | |
| 204 | === Merge a single change from master into a release branch === #cherrypick |
| 205 | The equivalent to Subversion's `svn merge -c <revision> .` is `git cherry-pick`. Use `git cherry-pick` to apply a single change from master to a release branch. To do this, look up the commit ID of the commit you want to pick: |
| 206 | {{{ |
| 207 | git log |
| 208 | # copy the commit ID |
| 209 | }}} |
| 210 | Switch to the target branch of the cherry pick: |
| 211 | {{{ |
| 212 | git checkout release_2_3 |
| 213 | }}} |
| 214 | Cherry-pick the commit. It is good practice to pass `-x` to `git cherry-pick`, which will automatically add a "Cherry picked from commit <commmitID>" line to the commit message of your cherry pick. You will have the option to modify the commit message, e.g. to describe why the backport was necessary. |
| 215 | {{{ |
| 216 | git cherry-pick -x <commitID> |
| 217 | }}} |
| 218 | Finally, push the new commit using |
| 219 | {{{ |
| 220 | git push origin <branchname> |
| 221 | }}} |
| 222 | |
| 223 | |
| 224 | |
| 225 | |
| 226 | |