Before you start
This text covers the use of Git for project collaboration and assumes knowledge of the information covered in the first part of this article: Learning the basics of git and the use of branches, read first to facilitate understanding of the text below.
Getting started
This article will use two fictional characters: Jessica, who has started a new project with a Git repository located at /home/jessica/project, and Jason, who has a user on the same system and wants to contribute to the project using the /home/jason/project-jessica directory.
Assuming that Jessica's Git repository has already been created, Jason starts by cloning Jessica's project into his contribution directory:
Jason
cd /home/jason/ git clone /home/jessica/project projeto-jessica
Then Jason makes his changes:
Jason
# (Edit files)
git commit -a
At the end he asks Jessica to apply his updates to the original repository. So Jessica does it:
Jessica
cd /home/jessica/project git pull /home/jason/project-jessica
This causes file updates made in the master branch of Jason's repository to be applied to the branch Jessica was in when she ran the command. The git pull command fetches the changes made from the remote repository and then merges them with the current branch.
If a conflict occurs, Jessica will have to resolve them locally in her directory, Git will still fetch the changes but will refuse to merge them until the corrections are made.
Jessica can check the changes made by Jason before merge them using the git fetch command:
Jessica
git fetch /home/jason/project-jessica git log -p HEAD..FETCH_HEAD
To see all the changes Jason has made since the project was cloned, she can use the command:
Jessica
gitk HEAD..FETCH_HEAD
Note that this two-dot range notation .. can be used with both the git log command and gitk. If Jessica wants to see both the changes she has made and the changes Jason has made since the project was cloned, she can use the ... three-dot range notation.
Jessica
gitk HEAD...FETCH_HEAD
If Jessica decides that there are no relevant changes made by Jason, she can continue working normally, while if Jason's history shows something she really needs, she can run git pull and continue working relying on the recent changes made by Jason.
Depending on the project, there may be a need to interact with the remote repository constantly. To facilitate this process, it is possible to create an alias:
Jessica
git remote add jason /home/jason/project-jessica
# And then
git fetch jason
The creation of the alias makes it possible to use the jason/master form to access information already fetched with the git fetch command, as in the example:
Jessica
git log -p master..jason/master
The command above shows a list of all the changes Jason has made since the first clone was made. After analyzing these changes, Jessica could apply them with:
Jessica
git merge jason/master
# or fetch from hers remote branches
git pull . remotes/jason/master
Later, Jason may want to fetch Jessica's latest updates and he does so with the remote:
Jason
git pull
As Jason's repository was cloned from Jessica's, you don't need to enter the path to the repository, Git already stores this information and uses it in the git pull command.
Jason
# Shows the path of the original project
git config --get remote.origin.url /home/jessica/project # Shows the configurations created by the git clone git config -l # Git keeps a copy of Jessica's master branch called origin/master git branch -r
# Jason can continue working from another machine using ssh git clone jessica.org:/home/jessica/project projeto-jessica
Exploring History
The Git history is represented as a series of commits that can be listed with the command:
git log commit 82407449e223ddfd0b8e9ef6df95c8cd156e82b1
Author: Jason <jason@email.org>
Date: Tue May 14 21:37:07 2024 -0300 merge: Added comment to the code
The code in the commit line can be used with the git show command for more details on this commit:
git show 82407449e223ddfd0b8e9ef6df95c8cd156e82b1
Using the beginning of the code also works, as long as it is unique, in this example using only the numbers below was enough.
git show 82407
Every commit has a parent that represents the previous state of the project.
# show the last commit of the current branch git show HEAD # last commit of the experimental branch git show experimental # to see the parent of HEAD git show HEAD^ # to see the grandparent of HEAD git show HEAD^^ # to see the great-great grandparent of HEAD git show HEAD~4 # merge commits may have more than one parent # show the first parent of HEAD (same as HEAD^) git show HEAD^1 # show the second parent of HEAD git show HEAD^2
Naming commits
You can name a commit with:
git tag v2.5 82407
It is now possible to refer to the commit that starts with 82407 using the name v2.5. In order for this name to be valid for other people you need to create a tag object and perhaps sign it, look for details on the git tag command for more information.
Names can be used in Git commands whenever you need to refer to a commit, examples:
# compare the current HEAD to v2.5 git diff v2.5 HEAD # create a new branch named "stable" based at v2.5 git branch stable v2.5 # reset your current branch and working directory to its state at HEAD^ git reset --hard HEAD^
Using range with git log
# commits between v2.5 and v2.6 git log v2.5..v2.6 # commits since v2.5 git log v2.5.. # commits from the last 2 weeks git log --since="2 weeks ago" # commits since v2.5 which modify Makefile git log v2.5.. Makefile
If the stable and master branches diverged from a common point some time ago:
# list commits made in the master but not in the stable branch git log stable..master # Opposite of the command above git log master..stable
The git log command has a weakness because it displays commits in list format. When the history has lines of development that diverged and then merged again, the order shown in the command is irrelevant.
In these cases, the gitk command is better for viewing histories.
# Commits from the last two weeks with changes to files in the drivers folder
gitk --since="2 weeks ago" drivers/
Using versions and filenames:
# Compares files in different versions git diff v2.5:Makefile HEAD:Makefile.in # View the file in a specific version git show v2.5:Makefile
Searching
The git grep command is used to search for strings in project files:
# Recursively searches for "import" in files in the current directory git grep "import" # Search for "import" in commit v2.5 git grep "import" v2.5
Final Thoughts
Adding up this tutorial and its first part should be enough to have a basic distributed revision control for your projects. Two simple ideas help you understand the depth and power of Git:
- The object database is the system used to store the history of your projects, files, directories and commits.
- The index file is a cache of the directory tree state that is used to create commits, check working directories and contain the various trees involved in a merge.