3. Git and Friends¶
One of the most important tools we use for collaboration at the lab is git. Git is a powerful tool which is generally easy to use, but sometimes it is possible to wind up in a weird state. This section of the document describes some intermediate level features of git which may be useful to the developer team. Developers may also want to read tricks for configuring git.
Warning
Invalid link.
When to Use the Powers of the Force¶
Sometimes changes are made which affect the past history of the local branch,
and when these changes are pushed to GitHub or another git server, they will be
rejected because the history on the server does not match the local history. A
common example is rebasing a branch to pick up new changes, which is detailed
further in the next section. The server can be made to accept these changes
with the --force
flag, but be careful! Force pushing is a dangerous
operation which will permanently rewrite history on the server. Junior
developers should consult more experienced colleagues before force pushing.
Developers must always use a fully qualified push command when force pushing,
naming both the git remote and branch. To force push to a branch named
branch
, run:
git push --force origin branch
Do NOT run:
git push --force
Resolving Merge Conflicts¶
Merge conflicts are an unfortunate reality when working with peers on a large
code base. They are easy to fix, but they must be fixed with care as the
functionality of the code may be unintentionally changed. Always run tests
before pushing a branch which had merge conflicts. Merge conflicts can happen
when running git merge source_branch
and when pulling or rebasing.
Git denotes merge conflicts with a series of angle brackets. After each series
of angle brackets is the name of the commit where the changes came from. In
this case, the code in the HEAD revision is older than the other revision, so
the code between << HEAD
and the equal signs should be removed. The line
with the angle brackets and the newer commit hash should also be removed. This
cannot be done automatically, because git doesn’t know which lines to include
or whether some combination of the lines should be included.
<<<<<<< HEAD
sortx_sql = sortx.aggregate.as_sql(qn, cn)
sorty_sql = sorty.aggregate.as_sql(qn, cn)
=======
sortx_sql = sortx.aggregate.as_sql(qn, cn)[0]
sorty_sql = sorty.aggregate.as_sql(qn, cn)[0]
>>>>>>> 692b8936b466d8c651bb1ab39e96ca98c7c4714b
To resolve merge conflicts when running git merge
, add the files which were
corrected, and then commit. That commit will have its title automatically
generated – do not change the title, but feel free to add more details to the
body of the commit about why the merge is happening.
Always run git grep '<<<'
and git grep '>>>'
before pushing code after
a merge conflict and run tests. It is possible that some merge-related code
escaped notice, and this should be fixed as soon as possible.
Rebasing and Squashing Commits¶
Often a developer will check out a new branch and while they are working on the branch, different changes will be merged into develop. To pick up changes on develop, check out the working branch and run the following:
$ git checkout fancy-changes
$ git rebase develop
This will add all of the changes merged into develop since the branches
diverged onto fancy-new-changes
.
Sometimes a series of commits should be combined into one large commit. This
can be useful when there were many “work in progress” commits which do not need
to clutter the git history. This is called squashing commits. First, find the
oldest commit hash which should be squashed using git log
(in this example,
the hash is abcde12345
). Then run:
$ git rebase -i abcde12345 # i stands for interactive
Git will open up the editor and provide detailed instructions on how to choose which commits to keep or combine.
Cherry Picking¶
Sometimes it will be necessary to move several commits from one branch to
another. This can be achieved simply by using git cherry-pick
. First, check
out the branch which the commits will be moved to. Then, find the hash
representing the commit using git log --all
. For example, if that hash were
abcde12345
, run:
$ git cherry-pick abcde12345
This will add commit abcde12345
to the current branch.
When Disaster Strikes¶
On occasion, disaster will strike, and it will appear that all has been lost. It
is important not to panic as such mistakes can often be resolved. As long as the
.git
folder is intact, git keeps a log of the changes made to the
repository. If a change is made which affects git history, the hash of the
commit previous to the change will be stored in the special file
.git/ORIG_HEAD
, for instance abcde12345
. To go back to that commit, run
git reset abcde12345
.
Miscellaneous Git Tips¶
- To undo the last commit, run
git reset HEAD~1
- To amend the last commit which has not been pushed and fix anything which
was forgotten, first add any files which were changed or forgotten, then run
git commit --amend
- To add part of a file, use
git add -p
and follow the interactive instructions - To delete a remote branch named
branch
, rungit push origin :branch
- To get a pretty view of git history, run
git log --graph --all
(some developers alias this togit-net
)