Themes
- Definitions
- Git ignore
- Company proxy tricks
- Minimal config variable to set to work in good conditions
- Clone repositories
- Git init repositories
- Partial clone and partial checkout
- Manage Branches (list, deletion, creation)
- Update Local projects
- Rebase
- Merge
- Git status
- Git log
- Retrieve branches information
- Stage files/folders
- Commit files/folders
- Undo commands
- Compare files/folders
- Conflict management
- Cherry-pick
- Git shell
- Manage Tags
- Manage tags and branches with no local reference
- Manage tracked repositories
- Manage GIT properties
- Store credentials
- Hooks
- Work with a forked repository
- Work with a repository fetched by a CI tool
- Rebase a forked repository
- List Git Objects
- List Git References
- Unix-Windows compatibility tricks
- Uncommon GIT commands but very useful
- Graphical viewers
- Export repository
Definitions
The repository :
you have the remote and the local repository. They contain the repository content (commits, tags, …).
By default the remote contains the whole content because that is the reference while for practical reasons the local that we get by cloning a remote repository contains by default only the default branch and commits associated to. We could of course update the local repository to retrieve anything stored in the remote.
The working tree :
Files and folder that we are currently working on. It is the area before that a file is managed by Git.
The index :
It is an area between our working tree/workspace and our local repository.
The index holds a snapshot of the content of the working tree, and it is this snapshot that is taken as the contents of the next commit (quoted from the official documentation).
Indeed, in Git, we don’t commit changes directly but we add them first in the index or stage area (while we could commit directly but that is generally unadvised).
Sometimes, we chain index adding and commits but some other times, we consider that area as a checkpoint to save our quite stabilized progress but not still enough to perform a commit.
The HEAD :
It is a pointer on the commit of the branch/revision of our working tree.
The origin :
It is the reference name given to the remote repository inside our local git repo.
In commands where we specify branch or commit, if we prefix it with origin
such as origin/foo-branch
it means
that we refer to the remote version of the branch. If we don’t prefix it, it means that we refer to the local version (git local repo) of the branch.
A ref :
A ref is anything pointing to a commit.
For example, branches (heads), tags, and branches.
We have refs stored in our git local repository and we have also refs stored on the git remote repository.
Refs are grouped by folder/category in .git/refs (local or server).
We have for example heads, remotes (in case of local repository), tags folders.
The general workflow :
(Not Git World) Git stage area Git repository Git repository Working tree -----> Index -----> local repository -----> remote repository add commit push
Git ignore and Git exclude
Git ignore
Apply the pattern relative to the directory level of the .gitignore file
If we specify a separator at the beginning or middle (or both) of the pattern.
Example:
/config
config/file.txt
Apply the pattern at any level below the .gitignore level.
If we specify no separator or a separator at the end of the pattern.
Example:
config
config/
Ignore the modifications of a local file/directory that exists in the remote repository
Edit the file .git/info/exclude
by adding a line specifying the exact location of the file/directory to ignore.
Example:
config/file.txt
The working tree cannot be aware of the change, so we need to force it in this way:
git update-index --assume-unchanged config/file.txt
Company proxy tricks
Disable the ssl verify globally (a trick. Favor the certification rather ):
git config --global http.sslVerify false
Disable the ssl verify for a command :
env GIT_SSL_NO_VERIFY=true git clone...
Or for the current shell instance :
export GIT_SSL_NO_VERIFY=true
git clone...
Set the http proxy :
git config --global http.proxy http://username:password@proxyHost:proxyPort
Set the https proxy :
git config --global https.proxy http://username:password@proxyHost:proxyPort
Setting an http proxy bypass (last entry below) by url along http and https proxy declaration:
[http] proxy = http://my.proxy.net:8080 [https] proxy = http://my.proxy.net:8443 [http "http://my.internalgitserver.com/"] proxy = "" |
Minimal config variables to set to work in good conditions
Who we are :
git config --global user.name "fooUsername"
git config --global user.email foo@bar.com
Remember credentials (to not repeat our creds at each interaction with the remote repo) :
git config --global credential.helper (store/manager/... choose the way that suits)
Diff and Merge tools :
…
Git init repositories
command:
git init
According to the version of git we use, the name of the default branch may be different:
– master
– main
Whatever, we can specify explicitly the default branch by setting the init.defaultBranch
configuration option(introduced in Git 2.28.0, released in July 2020).
For example:
git config --global init.defaultBranch main
– We can also specify the default branch name when we initialize a repository:
-b
--initial-branch=
Clone repositories
Clone a remote repository :
git clone http://mydomain/myProject.git [targetFolder]
By default the local git repo is stored under a directory named as the git repository.
To override it we could specify the local target folder at the end of the command.
Flags :
-b BRANCH_NAME :
make HEAD point to point to BRANCH_NAME instead of the cloned repository HEAD (that is generally master).
The args --bare
and --mirror
may be helpful in some specific cases.
--bare
will create only local tracking.
--mirror
will do as the default but by fetching all existing branches. So it is a way to copy the whole git repository.
More here :
https://stackoverflow.com/questions/3959924/whats-the-difference-between-git-clone-mirror-and-git-clone-bare
To disable the SSL verification during clone :
env GIT_SSL_NO_VERIFY=true git clone ...
Partial clone and partial checkout
Partial clone makes sense if we don’t need to retrieve all resources to work for :
– repositories with a lot of files
– repositories with big blob files
Pre-requirement : use a recent version of Git.
Version 2.30 and more recent are perfect.
It may work in a some way with older versions but with some potential limitations.
Clone a remote repository :
Helpful shallow clone flags :
--depth DEPTH
:
Shallow clone with a history truncated to the specified number of commits.
Implies --single-branch
unless --no-single-branch
is passed.
--filter=FILTER
Partial clone feature and request that the server sends a subset of reachable objects according to a given object filter.
For example, --filter=blob:none
will filter out all blobs (that are finally any file contents) until needed by Git
--sparse
Initialize the sparse-checkout
file so that the working directory starts with only the files in the root of the repository.
Default sparse-checkout file
The root directory is added as a parent pattern. The sparse-checkout file contains the following patterns:
/*
!/*/ |
This says « include everything in root, but nothing two levels below root. »
The sparse-checkout
file can be view or updated via the git sparse-checkout
command.
Pattern order :
Order matters, so the negative patterns are overridden by the positive patterns that appear lower in the file.
Examples :
Clone the default branch (master) of the myapp.git repository by fetching only the last commit history, without content checkout (blob:none), with only file at the root (so without its directories) :
git clone --depth 1 --filter=blob:none --sparse https://gitlab.com/foo/bar/myapp.git
Variant :
– add the -b foo-branch
flag to clone the foo-branch instead of the default (master).
List patterns to checkout of the current list:
git sparse-checkout list
Add a pattern to checkout in the current list :
git sparse-checkout add pattern
Ex: add the foo/bar/superFolder/
folder and all its children resources :
git sparse-checkout add foo/bar/superFolder
Variant ex: add the /foo/bar
folder but not directories inside (only files) that but
/foo/bar/a
full content folder and /foo/bar/b
full content folder :
git sparse-checkout add '!/foo/bar/*/' '/foo/bar' '/foo/bar/a' '/foo/bar/b'
Replace the whole current list to checkout by that pattern :
git sparse-checkout set pattern
Ex: replace all existing patterns to checkout by the foo/bar/superFolder/
folder and all its children resources : git sparse-checkout set foo/bar/superFolder
To keep the original pattern (root folder also without) while setting some new pattern(s) :
git sparse-checkout set '/*' '!/*/' 'myapp/bar'
Manage branches (list, deletion, creation)
List local branches:
git branch
Useful flags :
--sort=-KEY
: sort by
Ex: --sort=committerdate
See git-for-each-ref doc for available keys
-r
: list remote branches
-a
: list both remote and local branches
-d branchName
: delete a local branch
Use -D instead of -d to force delete
To delete a remote branch :
git push remoteName -d BranchName
remoteName is generally origin
.
To fetch local copies of all remotes branches :
Note : it doesn’t update local branches, nor create remote branches not existing in local
git fetch --all
Make the local repository (HEAD and working tree) to point to a specific branch:
git checkout branchName
(without specifying « remotes » or « origin » as prefix)
Create a local branch, point to that and push the remote upstream information:
git checkout -b branch-name
git push --set-upstream origin branch-name
For more recent version of git, we can use the switch command:
git switch -c branch-name
Manage tags
Two flavor of tags : lightweight (only a pointer on an existing commit) and annotated (tag with its own commit, object and checksum)
To list tags :
git tag
To fetch all tags from the remote :
git fetch origin --tags
To create an annotated tag :
git tag -a tagName -m "commit message"
To show details of the tag :
git show tagName
To push a tag on the remote server :
Beware similarly to branches, git push
doesn’t do that. You have to do :
git push origin tagName
Manage tags and branches with no local reference
In some specific circumstances :
– partial cloning
– missing refs in local git repository
Executing git checkout foo-branch
or git checkout tags/foo-tag
fails with a error like :
error: pathspec 'foo-branch' did not match any file(s) known to git. |
To fetch a specific branch from the remote :
git fetch origin refs/heads/foo:refs/heads/foo
The left part is the branch reference in the remote repository and the right part is the reference that we will use in your local repository.
Then we could checkout that :
git checkout foo
To fetch a specific tag from the remote :
git fetch origin refs/tags/foo:refs/tags/foo
The left part is the tag reference in the remote repository and the right part is the reference that we will use in your local repository.
Then we could checkout that :
git checkout tags/foo
Manage tracked repositories
Show the remote information (fetch and pull information) :
git remote -v
Changes the list of branches tracked by the named remote :
git remote set-branches REMOTE-REPO-NAME branchOne branchTwo ...
To track all branches:
git remote set-branches REMOTE-REPO-NAME '*'
Add the fooUrl as a remote repository for pushing (can add multiple if needed):
git remote set-url origin --push --add fooUrl
Pushing to two git remote repo and pulling from a single one should look like
[remote "origin"] url = http://gitlab.david.com:8585/gitlab-pipeline/maven-docker-separate-stages-example.git pushurl = https://gitlab.com/docker-david-projects/maven-docker-separated-stages-example.git fetch = +refs/heads/*:refs/remotes/origin/* pushurl = http://gitlab.david.com:8585/gitlab-pipeline/maven-docker-separate-stages-example.git |
Allow to pull when we change the repository origin url in the local repo
By default we cannot pull because the current and the previous histories are « unrelated ».
We can bypass it with --allow-unrelated-histories
such as :
git pull -X ours --allow-unrelated-histories
to keep also the current change
Update Local projects
Remote-tracking branches location : refs/remotes/
directory.
Update your remote-tracking branch :
git fetch [remote]
Merge your remote-tracking branch into our local branch:git merge
Both operations (Update + Merge remote-tracking into our local branch )
git pull [remote] [branch]
Notes about fetch/merge/pull :
1)when no remote is specified as argument, by default the origin remote will be used, unless there’s an upstream branch configured for the current branch.
2) fetch doesn’t update local branches under refs/heads (only the refs/remotes directory)
3) After fetching a remote branch in the local repo, we could compare that a local branch to that remote branch with :
git log localBranch..origin/remoteBranch
Rebase
git rebase
: Reapply commits on top of another base tip.
The flow is applying commits of the master (or any branch) to the target branch :
$ git checkout feature-x
$ git rebase master |
We could have errors if conflicts.
… todo git rebase –continue
And then merge that target branch into the branch we based on for the rebase :
$ git checkout master
$ git merge feature-x |
Merge
Sometimes the rebase command rises many conflicts to handle.
In this kind of cases it may be helpful to use instead of the merge command.
If you want update branch_old
with all changes from branch_new
:
git checkout branch_new
git merge -s ours branch_old
git checkout branch_old
git merge branch_new |
Hooks
– Default location : .git/hooks
of the current local git repo.
– To make it working on Linux : make them executable (x) and with a correct #!/bin/sh
prolog.
– To store the hook files in the source code : add a hooks
directory in the current branch with the desired content and set the core.hooksPath
property as global to always use that default folder :
git config --global core.hooksPath hooks
Work with a repository fetched by a CI tool
CI tools as Jenkins or Gitlab work very closely with Git remote repository that they clone locally.
But generally, the cloned repository is not fully configured to allow us to push anything on it.
For example : a CI job may need to update the version of the component during a release task (ex: 1.0-SNAPSHOT-> 1.0-SNAPSHOT->1.1-SNAPSHOT) on a specific branch, generally the master branch.
To achieve it, we need to update the cloned repository configuration.
Gitlab example : clone a repo with the oauth2 protocol
Requirements :
– Having generated a access token from the user settings/access tokens menu of gitlab.
– Store it as a variable of the CI/CD of the project/group, for example as GITLAB_MY_ACCESS_TOKEN variable.
Tag : stage: tag tags: - maven image: maven:3.6.3-openjdk-16 script: # CLONE WITH WRITE RIGHTS CONF - gitlabUrlWithCreds=$(echo $CI_PROJECT_URL | sed 's@http://@@' | xargs -I{} echo http://oauth2:${GITLAB_MY_ACCESS_TOKEN}@{}.git) - echo "gitlabUrlWithCreds=$gitlabUrlWithCreds" - git clone ${gitlabUrlWithCreds} repo-with-creds - cd repo-with-rights - git config user.email "gitlab pipeline" - git config user.name "gitlab pipeline" # Not mandatory but prevent warning output appearing since git 2.0 - git config push.default simple # Once configured, we could push anything. Ex: a tag - tagVersion=1.0 - git tag -a $tagVersion -m "Tagged by gitlab" - git push origin $tagVersion |
Jenkins example : use the fetched repo for the job and push with credentials (password encoded or not)
Requirements :
– Having two global variables defined to store user and password to push on gitlab. For example : JENKINS_USER and JENKINS_PASSWORD (these could be local but makes more sense to make them global for other projects use)
stage('Tag') { when { branch "master" } steps { script{ # We keep all but the https:// part GITLAB_PROJECT_URL_PROTOCOL_LESS = sh(returnStdout: true, script: "git config remote.origin.url | sed 's@https:\\/\\/@@'").trim() sh """ / Not mandatory but prevent warning output appearing since git 2.0 git config push.default simple // Not mandatory but may help git config http.sslVerify false git tag v${VERSION} git push https://${JENKINS_USER}:${JENKINS_PASSWORD}@${GITLAB_PROJECT_URL_PROTOCOL_LESS} v${VERSION} """ } } } |
Other use case: configure the fetched cloned repo once for all :
In the previous example, we specify the push on the credentials. It means that we may still need to specify them for next pushing operations.
To prevent that, we could configure once for all the local repo with a remote origin url containing credentials.
Ex : add the remote origin url with github:
git remote add origin https://{username}:{password}@github.com/{username}/project.git
Work with a forked repository
1) Fork a repository in your aera from GitHub with the UI.
2) Clone it like any repository with git clone.
3) Generally want to work on a local branch and not the master :
git checkout -b my-branch
4) Commit/push like usual.
Rebase a forked repository
At a time you may need to update your branch from the changes done in the original repository from the fork.
To do that you have to :
1) Add the original repository as a remote (first time only).
For its label, upstream is the norm.
git remote add upstream https://github.com/xxx/xxx.git
To check its value : git remote -v.
2) Update your local repository against the original repository :git fetch upstream
3) Squash your changes from the number of commit to rebase:
git rebase -i HEAD~n
or from a specific revision :
git rebase -i specificRevisionHash
3 Bis) If we need to solve some conflicts, we could use a strategy flag to mean keep theirs (the branch where I merged to) or mine (the reference branch for the rebase).
For example if upstream/master is the reference base for the rebase and that we want to keep the changes from the current branch :
git rebase -i -X theirs upstream/master
To retrieve the revision hash :
git merge-base my-branch upstream/master
Or more simply for 3) : git rebase -i upstream/master
4) In the edited file, replace pick by squash but for the first one
5) Perform the the local branch rebasing from the upstream/master: git rebase upstream/master
Better example : I want to rebase a branch in forked repo on the master branch of the original repo (the upstream)
1) Fetch upstream branches to the local branches : git fetch upstream
Here commits to the master of the upstream repo are stored in local in upstream/master.
2)To know new commits between my local branch and the upstream reference branch, I can do : git log mybranch..upstream/master
3) Rebase on master with squash : git rebase -i upstream/master
The pick prefix for the first comment and the next have to be prefixed by squash
3 bis) Or Rebase on master without squash : git rebase upstream/master
5) Force push branch on the origin (force if conflict) :
git push --force
Get content or type and size information of GIT objects
For cat-file : commits, tree or blob objects.
Output the size of an object :
git cat-file -s sha1OfTheObject
Output the content of an object :
git cat-file -p sha1OfTheObject
Output the type of an object :
git cat-file -t sha1OfTheObject
List objects from a revision :
git ls-tree --full-name -r myRevision
List all objects (objectId and file/folder name) whatever the branch:
git rev-list --objects --all
List all objects (objectId and file/folder name) of a specific branch:
git rev-list --objects my-branch
List all objects (objectId, type and size) of the local repository and sort them by size
git cat-file --batch-check --batch-all-objects | sort -n -k3
Show objects
Show the file content at a specific revision :
git show revisionOrBranch:foo/foobar/file.txt
Show the diff of a commit :
git show commitHash
List remote references
Command : git-ls-remote
Displays references available in a remote repository along with the associated commit IDs (last commit).
Two ways of using the command :
– without cloned repository
git ls-remote
– with cloned repository
git ls-remote https://mygit.com/foo.git
If authentication is required, we could use oauth2 token :
git ls-remote https://oauth2:myToken@mygit.com/foo.git
Helpful flags :
– List only tags : --tags or -t
– List only branches : --heads or -h
These options are not mutually exclusive.
– Do not show peeled tags (suffixed with ^{}
) or pseudorefs like HEAD in the output : --refs
Git status
Show detailed status of the current git :
– paths that have differences between the index file and the current HEAD commit ( files staged)
– paths that have differences between the working tree and the index file (files with changes but not still staged)
– paths in the working tree that are not tracked by Git (new files)
git status
Useful flags :
* short format : -s
* porcelain (may specify a version) or very short format : --porcelain
Git Log
Base command : git log [revision range=fooRange] [path(s)...]
By default it shows all commits leading to the current HEAD.
We could specify paths
to limit commit to these files.
We could specify revision range="from..to"
to show only commits in the specified revision range.
Useful flags :
-p -- file1 file2 ...
: show diffs between each commit for the specified files
--name-only
: show names of changed files
--all
: show commits of all /refs
--decorate
: show the branch name label
--graph
: draw a text-based graphical representation of the commit history
--pretty=format
: pretty-print the contents of the commit logs in a given format, where format
can be one of oneline, short, medium, full, fuller, email, raw, format:string and tformat:string
--abbrev-commit
: show only a partial prefix instead of the full 40-byte hexadecimal commit object name
--online
: shorthand for "--pretty=oneline --abbrev-commit"
--follow
: continue listing the history of a file beyond renames
Useful examples :
Log all with graph and date :
git log --all --graph --oneline --pretty=format:"%h%x09%an%x09%ad%x09%s"
Show all commits between two branches (local or remote)
Some examples :
git log HEAD..master
: commits between HEAD and local master
git log origin/foo..origin/bar
: commits between remote foo and remote bar
git log HEAD..origin/bar
: commits between HEAD and remote bar
git log origin/foo..foo
: log between remote foo and local foo
Retrieve branches information
shows logs of the reference (HEAD is the default) stored in the local repository with the localized date format :
git reflog --date=local
Find the better common ancestor (generally the most recent) for a merge :
git merge-base <commit> <commit>
We could use the commit id returned with git show --summary <commit>
to get more details about that commit.
Find commits not yet applied to the upstream branch :
git cherry -v fooUpstreamBranch [child branch]
If « child branch » is not specified, the current branch is used for that.
Compare files/folders
git diff common issues
Problem :
git diff
produces a lot of differences with ^M
at the end of each line.
Solution:
It is a windows issue which the break line char differs from the linux way.
To solve it, we could set a property that makes automatically the conversion to the Linux way :
git config --global core.autocrlf true
git difftool options
--dir-diff
: to perform the graphical comparison from a specific folder. It avoids the difftool app opening a window for each changed file that we need also to close to pass to the next file to compare.
Compare master (last local version) with origin/master (last version fetched locally from remote) :
git diff master origin/master
Compare index changes not still staged :
git diff
Compare only names of index changes not still staged :
git diff --name-only
-C
: Detect copies as well as renames. (looks for files only in the same changeset)
--find-copies-harder
: Detect copies as well as renames. (looks for files in any changeset)
--compact-summary
: Output a condensed summary of extended header information such as file creations or deletions (« new » or « gone », optionally « +l » if it’s a symlink) and mode changes (« +x » or « -x » for adding or removing executable bit respectively) in diffstat.
Compare a file in two distinct commits :
git difftool oneRevision otherRevision -- pathFile
Compare a file(or all files) between the HEAD and the previous commit :
git difftool HEAD~ HEAD
(add -- pathFile
to specify files)
Compare files between the current working copy and another branch:
git difftool FOO-BRANCH
Apply difftool on the working-copy only for a set of files modified between two commits:
Require 2 steps :
1) Find modified files between the two commits and store them into a file:
git diff --name-only fooCommit master > filesToCompare
2) Compare files between the current working copy and another branch only for these files :
git difftool --dir-diff master -- $(cat filesToCompare | awk '{files=files " " $1} END {print files}')
Configure Winmerge as graphical merge/diff tool :
Since Git 2.5 :
git config merge.tool winmerge
git config diff.tool winmerge
where winmerge is the winmerge executable that has to be set in the env var PATH.
We can specify any program able to compare two textual files.
Not working on my configuration.
So also I needed to specify the command absolute path :
git config --replace --global mergetool.winmerge.cmd "\"D:/applications-dev/WinMergePortable/winmerge.exe\" -e -u -dl \"Base\" -dr \"Mine\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\"" |
Disable prompt confirmation for difftool :
git config --global difftool.prompt false
Conflict management
– When git pull
informs a conflict.
Several cases :
1) If we want to merge manually files : git mergetool
2) If we want to keep local changes in case of conflict : git pull -X ours
3) If we want to keep upstream changes in case of conflict : git pull -X theirs
– When git merge remoteBranch
informs a conflict.
Several approaches:
1) We want to merge manually files : git mergetool
2) If we don’t want to handle the conflict file by file because too much conflicts and that we know which side (our or their) to keep, we could specify the restart the mege by selecting the conflict strategy for all files:
First we abort the merge : git merge --abort
Then :
* If we want to keep local changes in case of conflict : git merge remoteBranch -X ours
* If we want to keep upstream changes in case of conflict : git merge remoteBranch -X theirs
3) If we want to fix the conflict/unmerge paths for some file(s) by selecting the side (our or their) to keep :
* If we want to keep the local version : git checkout --ours -- file(s)
* If we want to keep the remote version : git checkout --theirs -- file(s)
– When git rebase
informs a conflict.
Several cases :
1) If we want to merge manually files : git mergetool
2) If we want to keep local changes in case of conflict : git rebase -i -X theirs BRANCH_REFERENCE
3) If we want to keep upstream changes in case of conflict : git rebase -X ours BRANCH_REFERENCE
Stage files/folders
Stage changes specific tracked or untracked files :
git add <file, file2, ...>
Stage changes all tracked files :
git add .
Stage changes tracked files matched to a path expression whatever its location :
git add *.xml
Useful add flags :
-n
: dry-run
-f
: allows staging ignored files
Specific staging commands :
Remove files/folders from the working tree and the index :
git rm <file, file2, ...>
Move files/folders in the working tree and the index :
git mv <file, file2, ...>
Commit files/folders
Commit staged files :
git commit -m "message..."
Replace the tip of the current branch by creating a new commit that has the same parents and author as the current one :
git commit --amend
Example : Create a new commit from the current one by using the same message :
git commit --amend --no-edit
Example : Create a new commit from the current one by changing the message :
git commit --amend -m "the new message"
Undo commands
checkout versus reset semantics
– With paths+commit args :
checkout and reset have a very closed behavior.
– With path arg only :
checkout updates the working tree with the content of the current HEAD.
reset updates the index with the content of the current HEAD.
– With commit arg only :
checkout switches to a new (detached) branch and update the head to that branch.
reset updates the index with the content of the commit and update the head to that.
undo FAQ
Undo working tree files :
git checkout .
Undo specific working tree files :
git checkout -- foo bar
Undo staged files :
git reset
Undo specific staged files :
git reset -- foo bar
Undo commited files and moves the HEAD to that commit :
git reset HEAD~1
Undo specific commited files (it doens’t move the HEAD to that commit):
git reset HEAD~1 -- foo bar
Further undo commands.
Set working tree files and index to a commit :
git checkout commit -- paths
Example :
git checkout 1545151de -- foo bar
It updates the a and b working tree files and the index to the specified commit
Hint : git reset with similar syntax may be used.
Set the current branch head to commit (may modify index and working tree to match).
git reset commit
By default, the mixed mode is used in that reset form.
Example : git reset 1545151de
Warn : Setting the current branch head to an old commit will make your branch to be behind commits.
Other Examples (to explain):
git reset HEAD~
git reset HEAD~2
git reset 12154df5454dfdf
git reset foobranch
git reset origin/foobranch
HEAD~
means the previous commit to the current HEAD, literally « head – 1 commit ».
HEAD~2
means « head – 2 commits » and so for…
12154df5454dfdf
is a commit revision
foobranch
is the local revision of foobranch.
origin/foobranch
is the remote revision of foobranch.
checkout :
Two main usages :
1)Switch to another branch and update HEAD and working tree according to when no paths arg is passed.
Syntax is : git checkout <fooBranch>
2)Update specified working tree files when paths arg is passed.
Syntax is : git checkout <fooBranch> -- file1 file2
To select all files : git checkout <fooBranch> -- .
The 1) way fails if working tree files or index are in conflict with the chosen branch. We need to stash or commit them first.
The 2) way restores from the index state, the specified resources of the working tree if we don’t specify a tree-ish but if we specify that, it restores both the index and the working tree.
reset :
Two main usages :
1) Reset current HEAD and optionally index and working tree to a specified commit
Syntax is : git reset <fooBranch>
That mode doesn’t allow to specify paths to reset.
About the mode :
Whatever the mode, it resets at least the head to the « commit ».
--soft
Only resets the head to the specific commit, just like all modes do (so don’t touch the index file neither the working tree)
--mixed
(default option)
Resets the index to the specific commit but not the working tree
--hard
Resets the index and the working tree.
WARN : Any changes to tracked files in the working tree since the commit are discarded.
2) Reset index for specified paths
Syntax is : git reset <fooBranch> -- file1 file2
That mode doesn’t allow to specify mode (the mixed mode will be used by default).
About untracked files:
These will not be removed when using reset or checkout .
Always use clean to remove them.
Remove untracked files :
git clean -f
Helpful clean flags :
-f
: force
-d
: remove untracked directories in addition to untracked files
-n
: dry-run
-X
: remove only resources ignored by git
Examples with reset and mode :
To reset only the head to the previous commit :
git reset --soft HEAD~
To reset head, index and working tree to the previous commit
git reset --hard HEAD~
To reset head, index and working tree to the last remote commit of the master branch :
git reset --hard origin/master
Rename a remote branch (copy the branch and then delete it) :
git branch -m old_branch new_branch
git push -u origin new_branch
git push origin :old_branch
Undo pushed commits (with no trace of undo) :
git reset sha1-commit
(make the head and the index to point to a specific commit)
or
git reset sha1-commit
(make the working tree, the head and the index to point to a specific commit)
Do/fix your modifications in the working directory…
git commit -m "message"
(commit)
git push -f origin branch-name
(force the push that will replace further commits by this one)
To force push from the detached HEAD to the remote :
git push -f origin HEAD:name-of-remote-branch
Revert a commit (with trace of undo) :
git revert sha1-commit
Alternative : specify range to revert from the HEAD back to the commit :
git revert --no-commit lastCommitToKeep..HEAD
Revert a merge-commit :
git revert -m <1or2> sha1-commit
Here the -m
flag specify the parent commit to keep after the revert(we have two of them in a merge).
Manage GIT properties
Display local GIT properties :
git config --list
Display global GIT properties :
git config --global --list
Set user email information :git config --global user.email "..."
Set user name information : git config --global user.name "Your Name"
Unset GIT properties :
If a single value for the entry :
git config --unset myProp
If multiple values for the entry :
git config --unset-all myProp
Open the global configuration :
git config --global --edit
To debug the config values and the files where they come from
Show all config values with their origin :
git config --global --list --show-origin --show-scope
List files and locations of config files :
git config --list --show-origin --show-scope
Store credentials
Linux Or Windows way with the store helper
Store and use the user and the password in plain text (information is stored in the home/.git-credentials
file (unix directory) :
For a local git repo :
git config --local credential.helper store
At each authentication need, git checks if the username and the url requested have a matching in that file and so uses that.
For global :
git config --global ...
Where credentials are lookup and stored ?
By default, in the files : ~/.git-credentials
or if not found in $XDG_CONFIG_HOME/git/credentials
To override that :
--file=foofile
Ex : git config --global credential.helper 'store --file ~/.my-credentials'
Whatever way, the file will have its filesystem permissions set to prevent other users on the system from reading it.
On Linux, we could clear the file (or the entry) that contains the credentials to not use any longer:
rm ~/.git-credentials
Windows way
To clear git cached password by the credential.helper:
Under Windows 7/10, we need to clear the credential from Credential Manager/Windows
Manager
Store user and password :
git config credential.helper wincred
Deprecated.
We should use :
git config --global credential.helper manager
But sometimes only the wincred value solves the issue.
Whatever OS
To benefit from secure storage provided by the OS :
we should use git-credential-cache
.
Switch to another user for a specific local git repo :
git config --local username NEW_USERNAME
(should do the same thing for the email and the name for consistency reasons)
We also often need to change the credential.username such as :
git config --local credential.username NEW_USERNAME
To disable credential.helper :
git config --global --unset credential.helper
It may be helpful before adding a new one.
Common issues :
Credentials are not cleared or git doesn't ask new credentials
1) Look at credential part in the global config and delete duplicate or weird things:
git config --global --edit
2) Clear the cached password (windows or Linux way)
Error when cloning, pulling or pushing a remote repository
remote: The project you were looking for could not be found. fatal: repository 'http://gitlab.david.com:8585/kubernetes-projects/spring-boot-docker-kubernetes-example.git/' not found |
Possible causes :
- error in the url of the project (may be the group name in the url). Check it with curl or your browser
- the user doesn't have the right to access to that project.
To fix : add to him the rights in the gitlab admin.
Unix-Windows compatibility tricks
Convert all files from the linux to the windows format (dos2unix to do the reverse) :
find . -type f -print0 | xargs -0 unix2dos
Uncommon GIT commands but very useful
When the "git clone" command gives this error :
fatal: unable to access 'https://github.com/...': error setting certificate verify locations: CAfile: C:/Program Files/Git/mingw32/libexec/ssl/certs/ca-bundle.crt CApath: none |
Check it in your filesystem and set the metadata in your git client by doing :
git config --system http.sslcainfo "absolute-path-to-ca-bundle.crt"
-----------------------------------------------------------------------------------------------------
Using meld as tool for merge and diff :
git config --global diff.tool meld
git config --global difftool.prompt false
You can also need to set the path of meld :
git config --global difftool.meld.path meldExecutablePath |
Beware, if you already configured another tool for these operations, you have to unset them as git config add and doesn't overwrite (same thing for mergetool).
Graphical viewers
To configure the difftool with meld :
# Add the following to your .gitconfig file. [diff] tool = meld [difftool] prompt = false [difftool "meld"] cmd = meld "$LOCAL" "$REMOTE" |
To configure the mergetool with meld :
[merge] tool = meld [mergetool "meld"] cmd = meld "$LOCAL" "$MERGED" "$REMOTE" --output "$MERGED" |
Predefined variables :
$LOCAL : the temporary file with the diff pre-image
$REMOTE : the temporary file with the diff post-image
$MERGED : the name of the file which is being compared
NB : in difftool "meld" and mergetool "meld" group, we could add the path entry of the command such as :
[difftool "meld"] cmd = meld "$LOCAL" "$REMOTE" path = c:/foo/meld.exe |
If diff-tool dir-diff fails with meld error :
--output option requires an argument
We could also configure difftool and mergetool from command line :
git config --global difftool.meld.cmd 'meld "$LOCAL" "$REMOTE"'
git config --global mergetool.meld.cmd 'meld "$LOCAL" "$MERGED" "$REMOTE" --output "$MERGED"'
Display the logs of all in a GUI graph :
gitk --all
Alias to shortcut common or long commands
Some common useful :
git config --global alias.br branch
git config --global alias.graph "log --graph --oneline"
git config --global alias.co checkout
git config --global alias.ci "commit -m"
git config --global alias.st status
git config --global alias.difd "difftool --dir-diff"
Git Shell
For Windows :
Use the Git Bash program.
For Linux :
Use the git-sh program. It is available via the package manager.
The completion may not work. In that case, we could edit the git-sh.sh program and source the git completion file,
something like that on ubuntu : . /usr/share/bash-completion/completions/git
Cherry pick
The cherry-pick command
Apply the changes introduced by some existing commits
Generally for commits of a specific branch to another one.
Syntax :
git cherry-pick [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] [-S[
Example :
I want to add the "foo" commit from branch A in the HEAD of the branch B.
git checkout B
git cherry-pick foo
Behavior of a cherry pick commit
- The cherry pick of a commit creates a new commit at the head of the target branch.
It means the sha1 and the message is a new one.
- the content of that commit is stick to the source branch. It is the difference between the commit-1 and the selected commit of the source branch.
The gitlab cherry-pick feature in merge requests
It allows to cherry pick the content of a terminated merge and so we could apply the created commit to propagate the result of the terminated merge in other branches
That cherry pick has the same characteristics than classic cherry pick command (see above)
Export repository
Export and import all branches for repository:
git clone --mirror <original_repository> cd <project>.git git remote add new_remote <new_repository> git push --all new_remote --force |