Installation¶
Windows¶
Git can be bundeled within other software, but it is best to install it separately (Git for Windows), because the installer contains a lot of important configuration options, that can have very inconvenient defaults.
Important options, that defaults to inpractical values:
- default text editor: defaults to vim
- default ssh client: defaults to bundled openssh (part of git bash)
- context menu integration: defaults to "Open in Git GUI" and "Open in Git Bash"
GitExtensions¶
GitExtensions is a GUI for Git that can make some operations easier, as diffs and branch structure are more clear when using a GUI.
The tool is intuitive it is only important to install git first.
Limitations¶
Unfortunatelly, GitExtensions does not support wordwrap in the diff view. There is an issue for that (closed, but never resolved).
Basics¶
The structure of a single git repository is displayed in the following picture:

Explanation of the picture:
- The working tree or working directory is the directory where the files are stored, typically the root directory of the project where the
.gitdirectory is located. - The index or staging area is a cache of all changes that are marked (staged) for the next commit. To stage a change, we need to call
git addcommand. Only files in the index are committed when we callgit commit. - The HEAD is a pointer to the last commit in the current branch.
The following scheme shows the operations that can be performed between the working tree, index, HEAD and the remote repository (blue arrows represent the typical workflow):

Configuration¶
For configuration, we can use the git config command. There are three levels of configuration:
- system: the configuration is applied to all users on the system. This configuration is set during the installation of git.
- global: the configuration is applied to all repositories of the current user. This configuration is set by the
--globalparameter. - local: the configuration is applied only to the current repository. This configuration is set by the
--localparameter.
To list the configuration, use the -l/--list parameter of the git config command. To list the configuration for a specific level, use the --system, --global, --local parameters.
To see the default value of a configuration, search in the git config documentation.
The git local configuration is stored in the .gitconfig file in the user's home directory. It can be edited by editing the file directly, or by calling git config command.
To display the active configuration in the command line, call:
git config --list
We can also show whether the configuration results from the system, user or local configuration file:
git config --list --show-origin
Basic Tasks¶
Rewrite remote with local changes without merging¶
git push -f
Go back to branch¶
git revert --no-commit 0766c053..HEAD
Untrack files, but not delete them locally:¶
git rm --cached <FILEPATH>
usefull params:
-r: recursive - deletes content of directories-ndry run
Branches¶
Branches are a way to manage parallel development. Each branch has a name and its own history. Typically (but not necessarily) a local branch tracks a remote branch with the same name.
To list the branches, we call git branch.
To switch to a branch, we call git checkout <branch name>. For that, the branch must exist locally. If it does not, we need to create it first:
- new branch from scratch:
git branch <branch name> - new branch from a remote branch:
git checkout --track origin/<REMOTE_BRANCH_NAME>- for this, the local repository needs to know about the remote branch. We may need to first
git fetchto update the remote branches.
- for this, the local repository needs to know about the remote branch. We may need to first
Renaming a branch¶
When renaming a branch we need to:
- rename the branch locally:
git branch -m <old name> <new name> - delete the old branch on the remote and push the new branch:
git push origin :<old name> <new name> - on all machines, change the remote branch to the new one
- on any other machine, rename the branch locally
Not that if the branch is protected or default, we cannot delete it directly. In that can, we need to remove the protection first, usually using the web interface of the particular remote.
Remote Repositories¶
Normally, we have a single remote repository, which is typically called origin. In this repository, we share the code with other developers. In this case we don't have to care about the remote repository handling, because the remote is automatically set to the repository we cloned from.
However, sometimes we need to work with multiple remote repositories. In this case, we have to add remotes manually and also specify the remote repository when we push or pull.
To add a remote, we call:
git remote add <remote name> <URL>
Where the remote and <URL> is is the link we use to clone the repository.
To list remotes, we call:
git remote -v
Then we need to specify the remote when we want to use a command that interacts with the remote repository and we want to use a remote other than the default one. For example, to push to a remote repository, instead of just git push, we call:
git push <remote name> <branch name>
Wildcards¶
Can be used in gitignore and also in some git commands. All described in the Manual. Usefull wildcards:
**/in any directory/**everything in a directory
Reverting¶
When we want to revert something using git there are multiple options depending on the situation. The commands are:
git checkoutfor overwriting local files with version from a specified tree (also for switching branches) andgit resetfor reverting commits and effectively rewriting the history.git read-tree: similar to checkout, but does not require a cleanup before
The following table shows the differences between the commands:
| Command | revrerts commits | overwrite local changes | delete files committed after <commit> |
|---|---|---|---|
git checkout <commit> |
no | yes | no |
git read-tree -m -u <commit> |
no | yes | yes |
git reset --soft <commit> |
yes | no | no |
git reset --hard <commit> |
yes | yes | yes |
To decide between the commands, the first consideration should be whether we want to preserve the history or not:
- we want to reach a specific state in the history and commit the changes as a new commit -> use
git checkoutorgit read-tree - we want to reach a specific state in the history and discard all changes after that point -> use
git reset
Keep the history¶
If we want to keep the history, there are still two options:
- we want to revert the whole working tree and also delete all files that were committed after the specified commit -> use
git read-tree -m -u <commit> - we want to revert the whole working tree, but keep all files that were committed after the specified commit -> use
git checkout <commit> - we want to revert only some files -> use
git checkout <commit> <filepath>
Using git checkout¶
To reset an individual file, call: git checkout <commit> <filepath>, to reset all files, call: git checkout <commit> ..
If the <commit> parameter is ommited, the local files will be overwritten by the HEAD.
Drop the history¶
Dropping the history can be useful in many cases. For example, we may commit some changes to the master, but then we realize that they belong to a branch. A simple solution is to create a branch, and then reset the master to the previous commit.
Note that if the wrong history was already pushed to the remote, we need to fix the history on the remote as well. This is done by force pushing:
git push -f
Removing specific files from history¶
Removing files from history can be done using multiple tools:
- bfg repo cleaner is the simplest tool. It can only select files by filename or size, but that is sufficient in most cases.
- git filter-repo is a more sophisticated successor to BFG. It can do almost anything possible. Nevertheless, it is less intuitive to operate, and it is harder to analyze its result.
- The
filter-branchcommand is the original tool for filtering git history. It is slow and problematic, so it should be used only if the above two tools are not available.
No matter of the used tool, before you begin:
- commit and push from all machines,
- backup the repository
Similarly, at the end:
- It is important not to merge (branch or conflitcs) that originated before the cleanup on other machines. Otherwise, the deleted file can be reintroduced to the history.
- Pull on other machines.
- Add the file to gitignore so that it wont be commited again
BFG¶
With BFG, only a file with specific filename can be deleted. It is not possible to use exact file path. To remove file by its name:
- remove the file locally
- clone the whole repo again with the
--mirroroption - on the mirrored repo, run the cleaner:
bfg --delete-files <FILENAME> - run the git commands that appears at the end of the bfg output
- run git push
Git filter-repo¶
The git filter-repo can be installed using pip: pip install git-filter-repo.
To remove file by its path:
- run the command with the
--dry-runparameter:git filter-repo --invert-paths --force --dry-run --path <PATH TO THE FILE TO BE REMOVED> - inspect the changes in
.git/filter-repodirectory:- Compare the files in KDiff3
- To skip the lines starting with
original-oid:- go to the file selection dialog
- click Configure
- to the line-matching preprocessor command, add:
sed 's/original-oid .*//' - click OK
- run the command without the
--dry-runparameter - force push the comman to the remote
git push origin --force --all- If the push is rejected by the remote hook, the master branch is probably protected. It has to be unprotected first in the repository config.
Appart from exact file path, we can also use:
- glob patterns:
--path-glob <GLOB PATTERN> - regex patterns:
--path-regex <REGEX PATTERN>
filter-branch¶
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <FILE>' --prune-empty --tag-name-filter cat -- --all
Merging¶
Syntax:
git merge <params> <commit>
Merge can be aborted at any time by calling git merge --abort. This resets the state of the repository.
Resolving Conflicts¶
Sometimes, the same file is changed on the same line in both branches we are merging. This is called a conflict. In this case, the conflicting content is marked with conflict markers:
<<<<<<< HEAD
This is the content of the file in the current branch
=======
This is the content of the file in the branch we are merging
>>>>>>> branch-a
We can resolve the conflict manually by editing the file, but more often, we want to use a merge tool. To do that, we can call:
git mergetool
The mergetool should be first configured, as the default one (vimdiff) is not very user friendly. To configure the mergetool, we edit the git configuration. The appropriate section is mergetool. It has a lot of options, but for typical mergetool, is enough to set the name, as the mergetool command for that specific merge tool is preconfigured in recent versions of git. Example of the .gitconfig file configured for KDiff3:
...
[mergetool "kdiff3"]
...
More information on git mergetool
Debugging mergetool¶
Sometimes, this error can occur after calling git mergetool:
git-mergetool--lib "command not found"
This means that the mergetool is misconfigured. Inspect the mergetool section in the .gitconfig file to find the error.
Merging Moved Files¶
Sometimes, it's necessary to tweak the command to help it locate the moved files. What can help:
-X rename-threshold=25: Changing the threshold is important for languages that changes the file content when moving it with automatic refactoring (especially important for Java files, which usually have tons of imports)-X ignore-space-change
Revert merge¶
git revert -m 1
Note that when the merge is reverted, the changes cannot be merged again, because they predates the revert!
Update¶
On windows, run: git update-git-for-windows
Pull Requests¶
Pull requests are a way to propose changes to the repository. The principle is as follows:
- Create a branch for the changes
- Commit the changes to the branch and push it to the remote
- Create a pull request on the remote, that suggest to merge the branch into the master branch
There are two possible scenarios:
- We have the permission to create a branch in the repository: in this case, we can create the branch directly on the remote
- We do not have the permission to create a branch in the repository: in this case, we have to:
- Fork the repository
- Clone the fork
- Create the branch locally in the fork
- Commit and push the changes to the fork
- Create a pull request from the fork to the original repository
Update pull request¶
If there are changes requested by the reviewer or we just forgot to add something, we can update the pull request by pushing the updates to the PR branch. The pull request will be automatically updated.
Showing the state in the command line¶
Mostly, we use GUI tools to show the state of the repository. However, sometimes we may need to show the state in the command line. The following commands can be useful:
git status: shows the state of the repositorygit log: shows the history of the repositorygit diff: shows the changes between the working tree and the index
git log¶
The git log shows the history of the repository. It is the analogue of the main view in GitExtensions. The most useful parameters are:
--oneline: shows the history in a compact form--graph: shows the history as a graph--all: shows the history of all branches, not only the current one--decorate: shows the names of the branches and tags, not only the commit hashes
GitHub¶
Creating a GitHub Release¶
To create a release:
- In the repository, under the
Releasesheading, clickCreate a new release - Click on the
Choose a tagdropdown - Select an existing tag or create a new one by filling the text field with the tag name and clicking
Create new tagbutton - Fill the
Release titleandDescriptionfields - Click
Publish release
GitHub CLI¶
Github has a CLI tool that can be used to interact with the repository. The tool can be installed from the GitHub CLI page. The main command is gh.
Typically, we need to authenticate the tool first by calling gh auth login. There are two options for authentication:
- browser authentication (default)
- token authentication: suitable for automation
- only the old token type can be used, the fine-grained tokens are not supported yet
Token Authentication¶
To authenticate using a token, we first need to create a token. The token settings are in Profile Settings -> Developer settings -> Personal access tokens.
Thereare two types of tokens:
- classic tokens: legacy access tokens
- fine-grained tokens: new access tokens with more granular permissions
Unless we need some specific permissions available only in the classic tokens, we should use the fine-grained tokens. This is the only option if we want to limit the scope of the token only to certain repositories.
To know what specific permissions are needed for a particular command, we can
- call the command with the
--helpparameter - read the documentation of the command
- run the command and read the error message
- ask
Then we can authenticate using the token. We can either:
- suply the token to the
gh auth logincommand:PowerShell gh auth login -h github.com -p ssh --skip-ssh-key --with-token <TOKEN> - or set the environment variable
GH_TOKENto the token value and call commands without authentication.
Managing realeases¶
Required permissions:
Read and Write access to code
To list releases, we can call the gh release view command.
To create a release, we can the gh release create command:
gh release create <TAG> --repo <REPO> --title <TITLE> --generate-notes
Here, the <REPO> is the full url of the repository, e.g. git@github.com:F-I-D-O/Future-Config.git
To delete a release, we can call the gh release delete command:
gh release delete <TAG> --repo <REPO> --cleanup-tag -y
Synchronizing a fork with the original repository¶
Required permissions:
Read and Write access to codeRead and Write access to workflows
To synchronize a fork with the original repository, we call:
gh repo sync <repo path> -b <branch name>
Here, the <repo path> is the path on the github.com domain, e.g. F-I-D-O/Future-Config. We can skip the -b <branch name> parameter if we want to synchronize the default branch.
Repository Migration¶
Git Large File Storage¶
Git Large File Storage is a system for storing and versioning large files in git repositories. One of the main use cases is to store experiment code and data in one place.
Note that with publicaly available repository hosting services like GitHub, the git LFS has severe limitations preventing almost any practical use. (see limitations)
First, install Git LFS in the system:
- Run the installer downloaded from the homepage
- run
git lfs installto initialize Git LFS
Then to use it in the repository:
- define for which files Git LFS should be used. For example, by extension:
- by running
git lfs track "*.png" - directly in the
.gitattributesfile: add the following line:*.png filter=lfs diff=lfs merge=lfs -text
- by running
- commit all work except the large files, including the changed
.gitattributesfile where the LFS filters are defined - check that the LFS status of the files is correct by running
git lfs ls-files(optional) - And that's it, now it should work. The only problematic part may be a slower push/pull performance.
Note that this simple procedure only affects new files. To affect existing files, we have to distinguish two cases:
- files added in the incorrect (text) mode that are not in the history yet (added, but not commited). These can be fixed by running
addwith the--renormalizeparameter:bash git add . --renormalize - files added before the LFS initialization (already in the history). These can be fixed by running with the
migratesubcommand.
The migrate subcommand¶
The migrate subcommand of the git lfs command was introduced to modify history. It has three subcommands:
import: converts files in history to the large file modeexport: converts files in history to the text modeinfo: shows the information about the files tracked by LFSbash git lfs migrate import --fixup
migrate import¶
This subcommand is used to convert files in history to the large file mode. There are two possible cases:
- we need to convert files that were committed before we start working with the LFS. We need to select such files manually, for example by specifying extensions:
bash git lfs migrate import --include "*.png,*.jpg,*.jpeg,*.gif,*.bmp,*.tiff,*.ico,*.webp" - we need to convert files that were committed after we start working with the LFS and the correct
.gitattributesfile was already in the history. This is much less likely, but it can happen. In this case, we can run the command with the--fixupparameter:bash git lfs migrate import --fixup
Git LFS Limitations¶
When working with git LFS, we need to consider some limitations:
- it slows down the push/pull operations
- we need to push early, not right before leaving the office
- the speed can be affected even without any new large files added
- There are further limitations depending on the remote repository hosting service:
- GitHub:
- maximum file size of 2GB
- forks cannot introduce any new large files
- Maximum bandwidth per month: 10GB
- GitHub:
Troubleshooting¶
GUI tools show no history, git log results in errors¶
This may be due to broken refs in .git/refs/. Possible errors:
your current branch appears to be broken-> the checked out branch is brokenfatal: bad object: <ref>-> the ref is broken
To fix the issue, we can copy the refs from the .git/logs/refs/:
- find the broken file in
.git/refs/ - find the corresponding file in
.git/logs/refs/ - copy the hash from the last line, second column and write it to the broken file
ssh key passphrase is required, despite the ssh key agent is running¶
This can have multiple reasons. To narrow down the issue, firtst check the ssh command separately.
If the ssh does not require a passphrase, but the git does, check whether git uses the same ssh:
- open git bash
- run
where ssh
The first (and only) path should be the same as the one that you are using with the agent, most likely the OpenSSH from Windows. If the git bash bundeled OpenSSH command is first, it means an incorrect git installation.