orphan: |
---|
Swift development has been based on SVN since its inception. As part of the transition to Git this document helps to address questions about how common SVN workflows we use today translate to their Git counterparts as well as to discuss Git workflow practices we plan on having — at least initially — after the Git transition. Notably we will follow a model where commits to trunk — which is the ‘master’ branch in Git — has commits land (in the common case) via rebasing instead of merging. This model is open to evolution later, but this mimics the workflow we have today with SVN.
The general SVN workflow consists of the following commands:
- Checkout: This means checking out/setting up a new repository.
- Update: Pulling the latest remote changes into a local repository.
- Committing: Committing a change to the remote repository.
- Reverting: Reverting a change from a remote repository.
- Browsing: Looking at commits.
This document will show how to translate these commands to Git and additionally how to configure Git. It assumes that one is attempting to manipulate a Git repository via bash in a terminal. A lot of information since this is supposed to be a short, actionable guide. For more information, please see the Git crash course guide for SVN users at <https://git-scm.com/course/SVN.html>
NOTE Whenever we say the Swift repository, we mean any repository in the Swift project.
For those who do not want to read the full document, use the following commands to perform a simple repo setup for the Swift repository:
$ git config --global user.name "<My Name>" $ git config --global user.email "<My Email>" $ mkdir swift-source && cd swift-soure $ git clone <LLVM_REPO_URL> $ git clone <CLANG_REPO_URL> $ git clone <SWIFT_REPO_URL> $ (cd swift && git config branch.autosetuprebase always) $ git clone <CMARK_REPO_URL> $ git clone <NINJA_REPO_URL>
Then to commit a new commit to the remote swift repository:
$ git commit $ git push origin master
and to pull new commits from the remote swift repository:
$ git pull origin master
In order to ease updating all repositories, consider using the script in './utils/update-checkout'. This will automate updating the repositories in the proper way.
Before beginning, we need to perform some global configuration of Git. Git includes a username/email of the committer in every commit. By default this is the current logged in user and the hostname of the machine. This is /not/ what one wants. We configure Git globally (i.e. across all repositories) to have our proper name and email by running the following commands:
$ git config --global user.name "<My Name>" $ git config --global user.email "<My Email>"
Normally if one wishes to checkout a repository in SVN, one would use a command like this:
$ SVN co <repository url> <local directory>
This would then checkout the latest revision from the repository specified by 'repository url' and place it into the directory 'local directory'. In Git, instead of checking out the repository, one clones the repository. This is done as follows:
$ git clone <repository url> <local directory>
This will cause Git to clone the repository at 'repository url' and check out the 'master' branch. The 'master' branch corresponds to 'trunk' in SVN. For more information about branching in Git please see <https://git-scm.com/course/SVN.html#branch>
Before beginning to commit though, we /must/ perform some default configuration of our repository to match the Swift repository default configuration by enabling default rebasing.
Once we have cloned the repository, we need to configure the repository for our use. Specifically we want to configure the swift repository so that we rebase whenever we update the repository (see the update section below for more details):
$ git config branch.autosetuprebase always
By default when updating, Git will attempt to merge the remote changes and your local changes. Ignoring what that sentence means, this is not an SVN-esque model. Instead we want any local changes that we have to be applied on top of any new remote changes. The 'branch.autosetuprebase' flag causes this to be done automatically when ever one updates the local repository.
In SVN, one updates your local repository by running the update command:
$ SVN update
In Git, instead of performing SVN update, one pulls from the remote repository:
$ git pull --rebase origin master
This will pull any new remote commits into your local repository and then replay your current local commits on top of those new commits.
By default the '--rebase' flag is not necessary for the Swift repository because it is configured to always rebase by setting the 'branch.autosetuprebase' flag (see the section 'Repository Configuration (Enabling Default Rebasing)' above).
In SVN, committing always means submitting changes to a remote repository. In Git, committing refers to the process of first telling Git to track a change by staging the change and then committing all staged changes into a change in the local repository. One can have many such commits. Then when one is ready, one pushes the new local changes to the remote repository. We go through these steps in more detail below:
In terms of replicating the SVN model, there are now two steps. In order to commit changes one first stages a changed file using 'git add':
$ git add <path>
Then once all changes that you want to be apart of the commit have been staged, a commit is created in the local repository via the 'commit' command:
$ git commit
As a shortcut to commit /all/ changes to local files that are already being tracked by Git to the local repository, you can use the '-a' command:
$ git commit -a
In both of these cases, an editor will pop up to accept a commit message. To specify a short commit message at the commandline, you can use the '-m' flag:
$ git commit -m 'My great commit message.'
In order to see the diff of changes that have not been staged, run the command:
$ git diff
To see all changes that have been staged, run the command:
$ git diff --staged
To get a diff for a specific revision/path, perform the following command:
$ git diff <revision> <path>
In order to get a more concise view of the files that have staged and or unstaged changes, run the command:
$ git status
In order to restore a file from the last revision, one uses the checkout command:
$ git checkout <path>
To restore a file to a specific revision, one must use a longer form of the command:
$ git checkout <revision> -- <path>
To unstage a file, one uses the 'reset' command:
$ git reset <path>
This tells Git to reset '<path>' in the staging area to the top of tree commit (which in Git is called 'HEAD'). In order to correct a mistake, you can pass the 'amend' flag to Git:
$ git commit --amend
This will cause all staged changes to be merged into 'HEAD'. Once one has made all the relevant commits, in order to push the changes to the remote repository the 'push' command is used:
$ git push origin master
If a different committer has committed changes such that there are remote commits that are not present locally, this will fail. In order to get around this issue, perform:
$ git pull --rebase origin master
in order to pull the new remote commits and replay your new commits on top. Then try to push again. See the 'Checkout' section above how to configure the local swift repository to always rebase allowing you to drop the '--rebase' flag.
In SVN reverting a commit implies performing a reverse merge. In Git, this is no longer true. Instead one now just uses the 'revert' command:
$ git revert <revision>
This will cause Git to perform the reverse merge of that revision for you against HEAD and bring up a message window for you to write a commit message. This will be autofilled in with the title of the commit that is going to be reverted and the revision number of that commit like so:
Revert "<FIRST LINE OF REVERTED COMMITS COMMIT MSG>" This reverts commit <REVISION>.
One can edit this message as one sees fit. Once this has been done, the revert will become a normal commit in your repository like any other commit. Thus to revert the commit in the remote repository, you need to perform a Git push:
$ git push origin master
This section explains how one can view Git changes. In order to view a history of all changes on a branch to the beginning of time use the 'log' command:
$ git log
This will for each commit show the following information:
commit <REVISION> Author: <AUTHOR NAME> <AUTHOR EMAIL> Date: <TIMESTAMP> <COMMIT MSG>
To see history starting at a specific commit use the following form of a Git log command:
$ git log <REVISION>
To see a oneline summary that includes just the title of the commit and its hash, pass the '--oneline' command:
$ git log --oneline
It will not show you what was actually changed in each commit. In order to see what was actually changed in a commit, use the command 'show':
$ git show
This will show the aforementioned information shown by Git log, but additionally will perform a diff against top of tree showing you the contents of the change. To see the changes for a specific commit, pass the revision to Git show:
$ git show <REVISION>