Best GIT practices
COMMIT RELATED CHANGES
A commit should be a wrapper for related changes. For example, fixing two different bugs should produce two separate commits. Small commits make it easier for other developers to understand the changes and roll them back if something went wrong. With tools like the staging area and the ability to stage only parts of a file, Git makes it easy to create very granular commits.
TEST CODE BEFORE YOU COMMIT
Resist the temptation to commit something that you «think» is completed. Test it thoroughly to make sure it really is completed and has no side effects (as far as one can tell). While committing half-baked things in your local repository only requires you to forgive yourself, having your code tested is even more important when it comes to pushing/sharing your code with others.
Branching is one of Git‘s most powerful features and this is not by accident: quick and easy branching was a central requirement from day one. Branches are the perfect tool to help you avoid mixing up different linesof development. You should use branches extensively in your development workflows: for new features, bug fixes, ideas…
Committing often keeps your commits small and, again, helps you commit only related changes. Moreover, it allows you to share your code more frequently with others. That way it‘s easier for everyone to integrate changes regularly and avoid having merge conflicts. Having few large commits and sharing them rarely, in contrast, makes it hard to solve conflicts.
WRITE GOOD COMMIT MESSAGES
Begin your message with a short summary of your changes (up to 50 characters as a gui- deline). Separate it from the following body by including a blank line. The body of your message should provide detailed answers to the following questions:
- What was the motivation for the change?
- How does it differ from the previous implementation?
Use the imperative, present tense («change», not «changed» or «changes») to be consistent with generated messages from commands like git merge.
AGREE ON A WORKFLOW
Git lets you pick from a lot of different workflows: long-running branches, topic branches, merge or rebase, git-flow… Which one you choose depends on a couple of factors: your project, your overall development and deployment workflows and (maybe most importantly) on your and your teammates‘ personal preferences. However you choose to work, just make sure to agree on a common workflow that everyone follows.
DO NOT COMMIT HALF-DONE WORK
You should only commit code when it‘s completed. This doesn‘t mean you have to complete a whole, large feature before committing. Quite the contrary: split the feature‘s implementation into logical chunks and remember to commit early and often. But don‘t commit just to have something in the repository before leaving the office at the end of the day. If you‘re tempted to commit just because you need a clean working copy (to check out a branch, pull in changes, etc.) consider using Git‘s «Stash» feature instead.
VERSION CONTROL IS NOT A BACKUP SYSTEM
Having your files backed up on a remote server is a nice side effect of having a version control system. But you should not use your VCS like it was a backup system. When doing version control, you should pay attention to committing semantically (see «related changes») – you shouldn‘t just cram in files.
FREE ONLINE BOOK
Workflow of Version Control
- Start a new project:
$ git initor work on an existing project:
$ git clone <remote-url>
The “git clone” command is used to download a copy of an existing repository from a remote server. When this is done, you have a full-featured version of the project on your local computer – including its complete history of changes.Executing the “git init” command in the root folder of your new project creates a new and empty Git repository. You’re ready to start getting your files under version control!
2. Work on your files
Modify, rename and delete files or add new ones. Do all of this in your favorite editor/ IDE / file browser – there‘s nothing to watch out for in this step! Files that aren’t yet under version control are called “untracked”, while files that your version control system already knows about are “tracked” files. A tracked file can either be “unmodified” (meaning it wasn’t changed since the last commit) or “modified” (meaning it has local changes since it was last committed
3. Keep the overview:
$ git status
The “git status” command tells you what happened since the last commit: which files did you change? Did you create any new ones or delete old ones?
4. Add files to the “staging area”:
$ git add <filename>
Only because a file was changed doesn’t mean it will be part of the next commit! Instead, you have to explicitly decide which changes you want to include. To do this, you add them to the so-called “Staging Area” with the “git add” command.
5. Keep the overview:
$ git status
Running the “git status” command right after a commit proves to you: only the changes that you added to the Staging Area were committed. All other changes have been left as local changes: you can continue to work with them and commit or discard them later.
6. Inspect the commit history:
$ git log
The “git log” command lists all the commits that were saved in chronological order. This allows you to see which changes were made in detail and helps you comprehend how the project evolved.
Branching & Merging
- Start a new feature:
$ git branch <new-branch-name>
Whenever you start a new feature, a new experiment or a new bugfix, you should create a new branch. In Git, this is extremely fast and easy: just call “git branch <new-branch-name>” and you have a new, separate context. Don’t be shy about creating new branches: it costs you nothing. We often have to work on multiple things in parallel: feature X, bugfix #32, feature Y… This makes it all too easy to lose track of where each change belongs. Therefore, it’s essential to keep these contexts separate from each other. Grouping related changes in their own context has multiple benefits: your coworkers can better understand what happened because they only have to look at code that really concerns them. And you can stay relaxed, because when you mess up, you mess up only this context. Branches do just this: they provide a context that keeps your work and your changes separate from any other context.
2. Switch contexts:
$ git checkout <new-branch-name>
To start working on a different context, you need to tell Git that you want to switch to it. You do this by “checking out” the branch with the “git checkout” command. Every commit you make, until you switch branches again, will be recorded in this branch and kept separate from your other contexts. At each point in time, you can only work in one context – the context of the currently checked out branch (which is also called the “HEAD” branch in Git). Your project’s working directory contains the files that correspond to this branch. When you check out a different branch (make it “HEAD”), Git replaces the files in your working directory with the ones that match this branch.
3. Integrate changes:
$ git merge <branch-to-integrate>
When your new feature is ready, you might want to integrate it into another branch (e.g. your production or testing branch). First, switch to the branch that is supposed to receive these changes. Then, call the “git merge” command with the name of the branch you want to integrate.
Sharing work via remote repositories
- Track a remote branch:
$ git checkout --track <remote/branch>or publish a local branch:
$ git push -u <remote> <local-branch>
If there’s an interesting remote branch that you want to work on, you can easily get your own local copy. Use the “git checkout” command and tell it which remote branch you want your new local branch to base off.
To share one of your local branches with your teammates, you need to publish it on a remote server with the “git push” command
2. Stay Up-To-Date About Remote Changes:
$ git fetch <remote>
When collaborating with others on a project, you’ll want to stay informed about their changes. The “git fetch” command downloads new changes from a remote repository – but doesn’t integrate them into your local working copy. It only informs you about what happened on the remote, leaving the decision on what to integrate to you. As Git is a so-called “decentralized” version control system, a remote repository is optional. In fact, everything we did until now happened on your local machine, in your local repository – no internet/network connection was necessary. However, if you want to collaborate with others, you need a remote repository on a server. You don’t have to share all of your work though: you can decide for each of your local branches if you want to share it or not.
3. Integrate Remote Changes:
$ git pull
To integrate new changes from the remote repository, you simply call “git pull”. This will update your current HEAD branch with new data from its counterpart branch on the remote. The changes will be directly merged into your local working copy.
4. Upload Local Changes to the Remote Server:
$ git push
To upload the local changes you made in your current HEAD branch, all you have to do is call “git push”.
Undoing things with GIT
--amend flag to change the last commit’s message or add a forgotten change.
Do you want to get rid of the last few commits and return to a previous state of your project? Use
git reset to roll back your project to a previous version!
Do you want to “undo” a single commit somewhere in the middle and leave later commits untouched? Use
git revert to create a new commit that contains “reverting” effects.
git reset to erase all of your current local changes. You can’t undo this!!
git checkout to undo local changes just in a certain file. You can’t undo this!
Top GIT commands
Clone an existing repository:
$ git clone ssh://firstname.lastname@example.org/repo.git
Create a new local repository:
$ git init
Changed files in your working directory:
$ git status
Changes to tracked files:
$ git diff
Add all current changes to the next commit:
$ git add .
Add some changes in <file> to the next commit:
$ git add -p <file>
Commit all local changes in tracked files:
$ git commit -a
Commit previously staged changes:
$ git commit
Change the last commit (Don‘t amend published commits!):
$ git commit --amend
Show all commits, starting with newest:
$ git log
Show changes over time for a specific file:
$ git log -p <file>
Who changed what and when in <file>:
$ git blame <file>
BRANCHES & TAGS
List all existing branches:
$ git branch -av
Switch HEAD branch:
$ git checkout <branch>
Create a new branch based on your current HEAD:
$ git branch <new-branch>
Create a new tracking branch based on a remote branch:
$ git checkout --track <remote/branch>
Delete a local branch:
$ git branch -d <branch>
Mark the current commit with a tag:
$ git tag <tag-name>
UPDATE & PUBLISH
List all currently configured remotes:
$ git remote -v
Show information about a remote:
$ git remote show <remote>
Add new remote repository, named <remote>:
$ git remote add <shortname> <url>
Download all changes from <remote>, but don‘t integrate into HEAD:
$ git fetch <remote>
Download changes and directly merge/integrate into HEAD:
$ git pull <remote> <branch>
Publish local changes on a remote:
$ git push <remote> <branch>
Delete a branch on the remote:
$ git branch -dr <remote/branch>
Publish your tags:
$ git push --tags
MERGE & REBASE
Merge <branch> into your current HEAD:
$ git merge <branch>
Rebase your current HEAD onto <branch> (Don‘t rebase published commits!):
$ git rebase <branch>
Abort a rebase:
$ git rebase --abort
Continue a rebase after resolving conflicts:
$ git rebase --continue
Use your configured merge tool to solve conflicts:
$ git mergetool
Use your editor to manually solve conflicts and (after resolving) mark file as resolved:
$ git add <resolved-file> $ git rm <resolved-file>
Discard all local changes in your working directory:
$ git reset --hard HEAD
Discard local changes in a specific file:
$ git checkout HEAD <file>
Revert a commit (by producing a new commit with contrary changes):
$ git revert <commit>
Reset your HEAD pointer to a previous commit …and discard all changes since then:
$ git reset --hard <commit>
…and preserve all changes as unstaged changes:
$ git reset <commit>
…and preserve uncommitted local changes:
$ git reset --keep <commit>