Difference between revisions of "Git/Git"
(37 intermediate revisions by the same user not shown) | |||
Line 4: | Line 4: | ||
=Terminology= | =Terminology= | ||
Git’s collaboration model is based on repository-to-repository interaction. In principle you push or pull commits from one repository to another. | Git’s collaboration model is based on repository-to-repository interaction. In principle you push or pull commits from one repository to another. | ||
:Staging Area - a place where we can group files together before we "commit" them to Git. | :Staging Area - a place where we can group files together before we "commit" them to Git. | ||
Line 10: | Line 9: | ||
File status | File status | ||
*staged: Files are ready to be committed. | *<tt>staged</tt>: Files are ready to be committed. | ||
*unstaged: Files with changes that have not been prepared to be committed. | *<tt>unstaged</tt>: Files with changes that have not been prepared to be committed. | ||
*untracked: Files aren't tracked by Git yet. This usually indicates a newly created file. | *<tt>untracked</tt>: Files aren't tracked by Git yet. This usually indicates a newly created file. | ||
*deleted: File has been deleted and is waiting to be removed from Git. | *<tt>deleted</tt>: File has been deleted and is waiting to be removed from Git. | ||
:HEAD - is a pointer that holds your position within all your different commits. By default HEAD points to your most recent commit, so it can be used as a quick way to reference that commit without having to look up the SHA. | :HEAD - is a pointer that holds your position within all your different commits. By default HEAD points to your most recent commit, so it can be used as a quick way to reference that commit without having to look up the SHA. | ||
Line 20: | Line 19: | ||
:Commands - are only relevant to directory that is tracked, it means it has .git/ tracking directory. Each time you use git commit changes are recorded in that directory. | :Commands - are only relevant to directory that is tracked, it means it has .git/ tracking directory. Each time you use git commit changes are recorded in that directory. | ||
settings = | |||
Specify your username and user email as these details are used each time you commit changes | Specify your username and user email as these details are used each time you commit changes | ||
<source lang=bash> | <source lang=bash> | ||
# Global user.name for every repository | |||
git config --global user.email "piotr@example.com" | git config --global user.email "piotr@example.com" | ||
git config --global user.name "Piotr" | git config --global user.name "Piotr" | ||
git config --system core.editor "/bin/vim" | git config --system core.editor "/bin/vim" | ||
#Unset configuration | # user.name for single repository | ||
git config user.email "vagrant@vagrant.local" | |||
git config user.name "vagrant" | |||
# or | |||
git config user.email "$(id -un)@$(hostname -f)" | |||
git config user.name "$(id -un)" | |||
# or add to the file myrepo/.git/config | |||
[user] | |||
email = vagrant@vagrant.local | |||
name = vagrant | |||
</source> | |||
Unset configuration item | |||
<source lang=bash> | |||
git config --global --unset http.proxy | git config --global --unset http.proxy | ||
</source> | </source> | ||
;Environment and configuration variables | |||
<source lang=bash> | <source lang=bash> | ||
man git-commit | man git-commit | ||
Line 43: | Line 53: | ||
# core.editor configuration variable, the VISUAL environment variable, or the EDITOR environment variable (in | # core.editor configuration variable, the VISUAL environment variable, or the EDITOR environment variable (in | ||
# that order). See git-var(1) for details. | # that order). See git-var(1) for details. | ||
</source> | |||
;Config files | |||
Git comes with a tool called <code>git config</code> that lets you get and set configuration variables that control all aspects of how Git looks and operates. These variables can be stored in three different places: | |||
* <code>/etc/gitconfig</code> - option <code>--system</code> writes to this global config, contains values for every user on the system and all their repositories. It also can contain individual user settings | |||
* <code>~/.gitconfig</code> or <code>~/.config/git/config</code> - specific to your user, <code>--global</code> option writes to this file | |||
* <code>.git/config</code> - local repository config file, in the Git directory of whatever repository you’re currently in, option <code>--local</code> is specific to that single repository. | |||
List configurations | |||
<source lang=bash> | |||
git config --global --list # list configuration, also can use --local or --system | |||
</source> | |||
Each level overrides values in the previous level, so values in <code>.git/config</code> trump those in <code>/etc/gitconfig</code>. | |||
On Windows systems, Git looks for the <code>.gitconfig</code> file in the <code>$HOME</code> aka <code>%profile%</code> directory <code>C:\Users\$USER</code> for most people. It also still looks for <code>/etc/gitconfig</code>, although it’s relative to the MSys root, which is wherever you decide to install Git on your Windows system when you run the installer. If you are using Git for Windows 2.x or later, there is also a system-level config file at <code>C:\ProgramData\Git\config</code>. This config file can only be changed by <code>git config -f <file></code> as an admin. | |||
= Credentials - Git helper = | |||
It's possible to call a script each time we need authentication. Ie. to automatically provide user and password we can create a script: | |||
<source lang=bash> | |||
cat ~/.git/git_helper.sh | |||
#!/bin/bash | |||
echo "username=$GIT_USER" | |||
echo "password=$GIT_PASS" | |||
</source> | |||
Then set it in global config: | |||
<source lang=bash> | |||
git config --global credential.https://bitbucket.example.com.helper=~/.git/git_helper.sh | |||
</source> | |||
Store credentials | |||
<source lang=bash> | |||
git config --global credential.helper store | |||
# creates plain text file `~/.git-credentials` where login+password are stored. | |||
</source> | </source> | ||
Line 82: | Line 133: | ||
== Git clone == | == Git clone == | ||
<source lang=bash> | |||
git clone git@github.com/yourLogin/repository.git # ssh cloning is using ssh-add -l | |||
git clone http://github.com/{project}/repository.git # clones using http/s, will prompt for username and password that exists in Github. | |||
</source> | |||
It | ;Submodules | ||
It a way to keep another Git repository within already existing projects. It's a way to to share a codebase with another independent codebase. | |||
<source lang=bash> | |||
# Add/initialize a submodule, then commit and push | |||
touch .gitmodules | |||
git submodule add git@github.com/yourLogin/repository-submodule.git --branch master | |||
# Note: Add `branch = master` as the --branch flag does not do anything | |||
# Recursively clone the project and any submodules | |||
git clone --recursive git@github.com/yourLogin/repository.git | |||
# Clean up a previously initialized submodule | |||
SUBMODULE=scripts | |||
PATH_TO_SUBMODULE=scripts | |||
git rm --cached $PATH_TO_SUBMODULE # (no trailing slash) as well as | |||
rm -rf $PATH_TO_SUBMODULE | |||
rm -rf .git/modules/$SUBMODULE/ | |||
</source> | |||
=== Move a repository from GitLab to other GitLab === | === Move a repository from GitLab to other GitLab === | ||
Let's assume you did clone a repo to your local machine. Notice you created --bare repository (can only push/fetch can't commit) | Let's assume you did clone a repo to your local machine. Notice you created --bare repository (can only push/fetch can't commit) | ||
Line 162: | Line 228: | ||
The files are in the Staging Area, are not in a repository yet. To store our staged changes we run: | The files are in the Staging Area, are not in a repository yet. To store our staged changes we run: | ||
git commit -m "Description of what we changed" | git commit -m "Description of what we changed" | ||
=== [http://karma-runner.github.io/6.3/dev/git-commit-msg.html Good Commit messages] === | |||
<source> | |||
# Format of the commit message | |||
<type>(<scope>): <subject> | |||
<body> | |||
<footer> | |||
# Example | |||
fix(middleware): ensure Range headers adhere more closely to RFC 2616 | |||
Add one new dependency, use `range-parser` (Express dependency) to compute | |||
range. It is more well-tested in the wild. | |||
Fixes #2310 | |||
</source> | |||
Allowed <type> values: # | |||
* feat (new feature for the user, not a new feature for build script) | |||
* fix (bug fix for the user, not a fix to a build script) | |||
* docs (changes to the documentation) | |||
* style (formatting, missing semi colons, etc; no production code change) | |||
* refactor (refactoring production code, eg. renaming a variable) | |||
* test (adding missing tests, refactoring tests; no production code change) | |||
* chore (updating grunt tasks etc; no production code change) | |||
Example <scope> values: init, runner, watcher, config, web-server, proxy | |||
== Add local repository to remote GitHub server == | == Add local repository to remote GitHub server == | ||
Line 193: | Line 287: | ||
What it does, is pulling the new code and applies your code on the top of it and creates a new commit. Your previous local commit will be deleted. | What it does, is pulling the new code and applies your code on the top of it and creates a new commit. Your previous local commit will be deleted. | ||
; | ;Interactive rebase, squash local commits and <code>push --force</code> to your remote branch rewriting history | ||
<source lang=bash> | |||
git log --oneline | |||
88c7520 (HEAD -> init, origin/init, init_bak) env | |||
acfce2f deb2 | |||
f07c09b deb2 | |||
342b8fc deb2 | |||
faaf152 deb2 | |||
8864ec2 deb2 | |||
5b6364f deb2 | |||
baf74f7 deb2 | |||
811c580 deb | |||
7015b0c deb | |||
6f4b90f deb | |||
49429bc deb | |||
9c096a1 deb1 | |||
ed675c3 new backend file | |||
89c262f backend file | |||
dc27559 param interpolation | |||
7707b98 pref lib | |||
dafd765 region removed from pipe # <- 18 commits to rebase(squash) into this 18th commit | |||
ccc871f libs prefix | |||
380a40c init | |||
1a0c2ee (origin/master, master) init | |||
# Decide, how many commits you wish to rebase, eg. 18 | |||
git rebase HEAD~18 --interactive | |||
pick dafd765 region removed from pipe | |||
pick 7707b98 pref lib | |||
pick dc27559 param interpolation | |||
pick 89c262f backend file | |||
pick ed675c3 new backend file | |||
pick 9c096a1 deb1 | |||
pick 49429bc deb | |||
pick 6f4b90f deb | |||
pick 7015b0c deb | |||
pick 811c580 deb | |||
pick baf74f7 deb2 | |||
pick 5b6364f deb2 | |||
pick 8864ec2 deb2 | |||
pick faaf152 deb2 | |||
pick 342b8fc deb2 | |||
pick f07c09b deb2 | |||
pick acfce2f deb2 | |||
pick 88c7520 env | |||
# Rebase ccc871f..88c7520 onto f07c09b (18 commands) | |||
# Commands: | |||
# p, pick <commit> = use commit | |||
# r, reword <commit> = use commit, but edit the commit message | |||
# e, edit <commit> = use commit, but stop for amending | |||
# s, squash <commit> = use commit, but meld into previous commit | |||
# f, fixup <commit> = like "squash", but discard this commit's log message | |||
# x, exec <command> = run command (the rest of the line) using shell | |||
# b, break = stop here (continue rebase later with 'git rebase --continue') | |||
# d, drop <commit> = remove commit | |||
# l, label <label> = label current HEAD with a name | |||
# t, reset <label> = reset HEAD to a label | |||
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] | |||
# . create a merge commit using the original merge commit's | |||
# . message (or the oneline, if no original merge commit was | |||
# . specified). Use -c <commit> to reword the commit message. | |||
# These lines can be re-ordered; they are executed from top to bottom. | |||
# If you remove a line here THAT COMMIT WILL BE LOST. | |||
# However, if you remove everything, the rebase will be aborted. | |||
# Note that empty commits are commented out | |||
</source> | |||
So now, I want to squash deleting commit messages, thus I use 'f, fixup'. Note, teh commits are in the reversed order, most recent is at the bottom. I want to melt up all commits from most recent into one of the first ones <code>pick dafd765 region removed from pipe</code>. Thus changing <code>pick</code> to <code>fixup</code> as below, then save to apply. | |||
<source lang=bash> | |||
pick dafd765 region removed from pipe # <- pick the oldest commit you wish to rebase(squash) on top of it | |||
fixup 7707b98 pref lib | |||
fixup dc27559 param interpolation | |||
fixup 89c262f backend file | |||
fixup ed675c3 new backend file | |||
fixup 9c096a1 deb1 | |||
fixup 49429bc deb | |||
fixup 6f4b90f deb | |||
fixup 7015b0c deb | |||
fixup 811c580 deb | |||
fixup baf74f7 deb2 | |||
fixup 5b6364f deb2 | |||
fixup 8864ec2 deb2 | |||
fixup faaf152 deb2 | |||
fixup 342b8fc deb2 | |||
fixup f07c09b deb2 | |||
fixup acfce2f deb2 | |||
fixup 88c7520 env | |||
</source> | |||
When completed you should see similar | |||
<source lang=bash> | |||
git rebase HEAD~18 --interactive | |||
Successfully rebased and updated refs/heads/init. | |||
# Notice change in the history log | |||
git log --oneline | |||
f7ec963 (HEAD -> init) region removed from pipe | |||
ccc871f libs prefix | |||
380a40c init | |||
1a0c2ee (origin/master, master) init | |||
</source> | |||
Push force, to avoid <code>Updates were rejected because the tip of your current branch is behind</code> | |||
<source lang=bash> | |||
git push --force | |||
... | |||
+ 88c7520...f7ec963 init -> init (forced update) | |||
</source> | |||
= Updating a local repo from a Remote Repository = | = Updating a local repo from a Remote Repository = | ||
Line 241: | Line 445: | ||
It’s important to note that this stages the file, too! | It’s important to note that this stages the file, too! | ||
=== Undoing changes | === [https://stackoverflow.com/questions/3293531/how-to-permanently-remove-few-commits-from-remote-branch permanently remove few commits from remote branch] === | ||
==== Undo a merge that hasn't been pushed yet ==== | You <code>git reset --hard</code> your local branch to remove changes from working tree and index, and you <code>git push --force</code> your revised local branch to the remote. (other solution here, involving deleting the remote branch, and re-pushing it) | ||
=== Undoing changes === | |||
==== In a local branch ==== | |||
===== Undo a merge that hasn't been pushed yet ===== | |||
Make your local master branch look identical to origin/master. It is possible that origin/master may be ahead of your local master just previous to the merge by some commits, in that case this might not give the desired results | Make your local master branch look identical to origin/master. It is possible that origin/master may be ahead of your local master just previous to the merge by some commits, in that case this might not give the desired results | ||
(master)$ git reset --hard origin/master | (master)$ git reset --hard origin/master | ||
Line 251: | Line 460: | ||
With newer Git versions, if you have not committed the merge yet and you have a merge conflict: | With newer Git versions, if you have not committed the merge yet and you have a merge conflict: | ||
git merge --abort | git merge --abort | ||
==== Undo a failed merge ==== | |||
Option 1 - <code>git clean</code> | |||
<source lang=bash> | |||
# Show files that git would purge if you weren't doing a dry run: | |||
git clean -ndx | |||
# Purge all those files from your working tree: | |||
git clean -fdx | |||
# also removes all files that aren't being tracked, including ignored files | |||
# | -f force | |||
# | -n dry run | |||
# | -d recurse also into untracked directories | |||
# | -x don’t use the standard ignore rules, allows removing all untracked files | |||
</source> | |||
Option 2 - Abort <code>git merge</code> | |||
<source lang=bash> | |||
git merge --abort | |||
# | --abort Abort the current conflict resolution process, and try to reconstruct the pre-merge state. | |||
</source> | |||
====Resetting branch pointer without changing files==== | ====Resetting branch pointer without changing files==== | ||
This command will move the current branch pointer back by one commit | This command will move the current branch pointer back by one commit | ||
<source lang=bash> | |||
git reset HEAD~1 | |||
</source> | |||
You can check the result by running <tt>git show</tt> which will give you the details of the most recent commit on the branch. Running it before and after resetting the branch pointer should show how it moved to the previous commit. Note that the files stay in the state of your undone commit and will therefore be marked as modified (or added, deleted) again. I think of this command as moving changes from the last commit back to the staging area. | You can check the result by running <tt>git show</tt> which will give you the details of the most recent commit on the branch. Running it before and after resetting the branch pointer should show how it moved to the previous commit. Note that the files stay in the state of your undone commit and will therefore be marked as modified (or added, deleted) again. I think of this command as moving changes from the last commit back to the staging area. | ||
Line 260: | Line 493: | ||
====Set back the current branch by one commit and undo all changes from that commit==== | ====Set back the current branch by one commit and undo all changes from that commit==== | ||
<source lang=bash> | |||
git reset --hard HEAD~1 | |||
</source> | |||
This will completely wipe out the most recent commit from the current branch. I am not sure if the actual commit data is also deleted, but it will definitely no longer be part of the current branch and your repository should look exactly like one commit ago. | This will completely wipe out the most recent commit from the current branch. I am not sure if the actual commit data is also deleted, but it will definitely no longer be part of the current branch and your repository should look exactly like one commit ago. | ||
Line 266: | Line 502: | ||
====Amending the most recent commit==== | ====Amending the most recent commit==== | ||
There is a flag for the commit command itself that allows you to pack changes on top of the most recent one. Instead of creating a new commit, Git will replace the most recent one. The new comment message will override the previous one. | There is a flag for the commit command itself that allows you to pack changes on top of the most recent one. Instead of creating a new commit, Git will replace the most recent one. The new comment message will override the previous one. | ||
<source lang=bash> | |||
git commit --amend -m "New commit" | |||
</source> | |||
== | ====Undo Last Commit with git revert==== | ||
In order to revert the last Git commit, use the “git revert” and specify the commit to be reverted which is “HEAD” for the last commit of your history. | |||
The “git revert” command is slightly different from the “git reset” command because it will record a new commit with the changes introduced by reverting the last commit. | |||
<source lang=bash> | |||
git revert HEAD | |||
</source> | |||
== Remove files == | == Remove files == | ||
Line 287: | Line 528: | ||
git diff aws-subnet | git diff aws-subnet | ||
= | = git worktree = | ||
Check out a different branch in your repository, in a seperate directory. It looks like and is used like a regular git repository, but it allows you to have multiple active working trees in the same repository at the same time. | |||
;Add worktree | |||
git worktree add ../another-directory | |||
This will create a new worktree in another-directory, which is a sibling of the current directory. At the same time it will also create a new branch with the same name as the directory. | |||
; Specifying branch name | |||
git worktree add -b new-branch ../another-directory | |||
git worktree add ../another-directory existing-branch|commit | |||
Remove the worktree | |||
git worktree remove ../another-directory | |||
All your commited changes will be present in the main repository, as if you had done the work there. | |||
= Git diff - preview a file changes = | |||
Compare 2 branches | |||
* [https://devconnected.com/how-to-compare-two-git-branches/ Compare 2 branches] | |||
The diff command does not work on symbolic links, you need you point to the hard file. | The diff command does not work on symbolic links, you need you point to the hard file. | ||
Changes between commits, ^ - last commit, ^^ - 2 commits and so on | Changes between commits, ^ - last commit, ^^ - 2 commits and so on | ||
Line 300: | Line 566: | ||
git diff MY-PROGS/TEST.PROG.A (?) | git diff MY-PROGS/TEST.PROG.A (?) | ||
Navigate with Space (next page), Enter (next line), B (previous page), Q (quit) | Navigate with Space (next page), Enter (next line), B (previous page), Q (quit) | ||
Show all historical commits against the file | Show all historical commits against the file | ||
git log -- [filename] | git log -- [filename] | ||
git log --follow -p -- [filename] #show content/diff | git log --follow -p -- [filename] #show content/diff | ||
Show the file content/diff commits history | Show the file content/diff commits history | ||
gitk [filename] | gitk [filename] | ||
Preview a file between branches | Preview a file between branches | ||
git diff ''mybranch branch_to_compare'' -- ''folder/file_to_compare.txt'' | git diff ''mybranch branch_to_compare'' -- ''folder/file_to_compare.txt'' | ||
Show a file form a different branch | Show a file form a different branch | ||
Line 335: | Line 605: | ||
git config merge.conflictstyle diff3 | git config merge.conflictstyle diff3 | ||
git config mergetool.prompt false | git config mergetool.prompt false | ||
= Set all branches to be tracked = | = Set all branches to be tracked = | ||
Line 361: | Line 620: | ||
https://api.github.com/user/repos | grep -o 'https://github.com[^\"]*.git'"</nowiki> | https://api.github.com/user/repos | grep -o 'https://github.com[^\"]*.git'"</nowiki> | ||
= | = Keep empty directory in a repository = | ||
Although it's not supported by design to track an empty directories you can add . | Although it's not supported by design to track an empty directories you can add empty <code>.gitkeep</code> file more as convention but this can be any empty file. | ||
=Tags= | =Tags= |
Latest revision as of 16:18, 31 October 2022
Install
sudo apt-get install git
Terminology
Git’s collaboration model is based on repository-to-repository interaction. In principle you push or pull commits from one repository to another.
- Staging Area - a place where we can group files together before we "commit" them to Git.
- Commit - is a snapshot of our repository. This way if we ever need to look back at the changes we've made (or if someone else does), we will see a nice timeline of all changes.
File status
- staged: Files are ready to be committed.
- unstaged: Files with changes that have not been prepared to be committed.
- untracked: Files aren't tracked by Git yet. This usually indicates a newly created file.
- deleted: File has been deleted and is waiting to be removed from Git.
- HEAD - is a pointer that holds your position within all your different commits. By default HEAD points to your most recent commit, so it can be used as a quick way to reference that commit without having to look up the SHA.
- Branching - are what naturally happens when you want to work on multiple features at the same time. You wouldn't want to end up with a master branch which has Feature A half done and Feature B half done. Rather you'd separate the code base into two "snapshots" (branches) and work on and commit to them separately. As soon as one was ready, you might merge this branch back into the master branch and push it to the remote server.
- Commands - are only relevant to directory that is tracked, it means it has .git/ tracking directory. Each time you use git commit changes are recorded in that directory.
settings =
Specify your username and user email as these details are used each time you commit changes
# Global user.name for every repository git config --global user.email "piotr@example.com" git config --global user.name "Piotr" git config --system core.editor "/bin/vim" # user.name for single repository git config user.email "vagrant@vagrant.local" git config user.name "vagrant" # or git config user.email "$(id -un)@$(hostname -f)" git config user.name "$(id -un)" # or add to the file myrepo/.git/config [user] email = vagrant@vagrant.local name = vagrant
Unset configuration item
git config --global --unset http.proxy
- Environment and configuration variables
man git-commit #ENVIRONMENT AND CONFIGURATION VARIABLES # The editor used to edit the commit log message will be chosen from the GIT_EDITOR environment variable, the # core.editor configuration variable, the VISUAL environment variable, or the EDITOR environment variable (in # that order). See git-var(1) for details.
- Config files
Git comes with a tool called git config
that lets you get and set configuration variables that control all aspects of how Git looks and operates. These variables can be stored in three different places:
/etc/gitconfig
- option--system
writes to this global config, contains values for every user on the system and all their repositories. It also can contain individual user settings~/.gitconfig
or~/.config/git/config
- specific to your user,--global
option writes to this file.git/config
- local repository config file, in the Git directory of whatever repository you’re currently in, option--local
is specific to that single repository.
List configurations
git config --global --list # list configuration, also can use --local or --system
Each level overrides values in the previous level, so values in .git/config
trump those in /etc/gitconfig
.
On Windows systems, Git looks for the .gitconfig
file in the $HOME
aka %profile%
directory C:\Users\$USER
for most people. It also still looks for /etc/gitconfig
, although it’s relative to the MSys root, which is wherever you decide to install Git on your Windows system when you run the installer. If you are using Git for Windows 2.x or later, there is also a system-level config file at C:\ProgramData\Git\config
. This config file can only be changed by git config -f <file>
as an admin.
Credentials - Git helper
It's possible to call a script each time we need authentication. Ie. to automatically provide user and password we can create a script:
cat ~/.git/git_helper.sh #!/bin/bash echo "username=$GIT_USER" echo "password=$GIT_PASS"
Then set it in global config:
git config --global credential.https://bitbucket.example.com.helper=~/.git/git_helper.sh
Store credentials
git config --global credential.helper store # creates plain text file `~/.git-credentials` where login+password are stored.
Commands
git init
Initialize repository by
git init
This creates 3 local repositories:
- your working directory (represented in blue in image )
- Staging area (brown)
- Local repository (green)
They represent the life-cycle of working with git. every file exists in blue, if you add the file you tell git that you want to keep tracking the file, so every modification will be saved in brown and if you commit that change this modification will remain forever and you could get it back whenever you want. Because it will be saved in green . Purple (purple repository) represent Github or Gitlab remote repositories.
Useful operations
Fetch the reference to the pull request based on its ID number, creating a new branch in the process
git clone https://github.com/ecomm-integration-ballerina/kubernetes-cluster.git git fetch origin pull/<PR-id>/head:<branch-name> git fetch origin pull/6/head:update-to-u18 # 6 it's PR id #6 here $ git branch * master update-to-u18
Update remote repository reference
List your existing remotes in order to get the name of the remote you want to change.
git remote -v origin https://github.com/USERNAME/REPOSITORY.git (fetch) origin https://github.com/USERNAME/REPOSITORY.git (push)
Change your remote's URL from HTTPS to SSH with the git remote set-url command. This command also accepts https:// url.
git remote set-url origin git@github.com:USERNAME/REPOSITORY.git
Git clone
git clone git@github.com/yourLogin/repository.git # ssh cloning is using ssh-add -l git clone http://github.com/{project}/repository.git # clones using http/s, will prompt for username and password that exists in Github.
- Submodules
It a way to keep another Git repository within already existing projects. It's a way to to share a codebase with another independent codebase.
# Add/initialize a submodule, then commit and push touch .gitmodules git submodule add git@github.com/yourLogin/repository-submodule.git --branch master # Note: Add `branch = master` as the --branch flag does not do anything # Recursively clone the project and any submodules git clone --recursive git@github.com/yourLogin/repository.git # Clean up a previously initialized submodule SUBMODULE=scripts PATH_TO_SUBMODULE=scripts git rm --cached $PATH_TO_SUBMODULE # (no trailing slash) as well as rm -rf $PATH_TO_SUBMODULE rm -rf .git/modules/$SUBMODULE/
Move a repository from GitLab to other GitLab
Let's assume you did clone a repo to your local machine. Notice you created --bare repository (can only push/fetch can't commit)
git clone --bare git@old-github.com/yourLogin/repository.git
Then you create an empty repo in the new GitLab called yourRepoName, so you can upload/push to it
cd repository.git git remote remove origin #add new remote GitLab repository before push git remote add newgitlab git@new-github.com/yourLogin/yourRepoName.git git push --mirror newgitlab #push all branches and tags to new remote
This process also allows to change the project/repository name although you do not need to.
Create bare repository, central storage, GitLab repo
git init --bare project.git #this will create a folder called project.git (bare repository)
--bare
flag creates a repository that doesn’t have a working directory, making it impossible to edit files and commit changes. Ultimately it creates the central repository for developers to push/commit their changes. GitLab new projects are created this way, become remote repository. Think of --bare as a way to mark a repository as a storage facility, opposed to a development environment.
Putting the Bare Repository on a Server
In preparation, you create a copy of project folder and place them in project.git pre-setup --bare
folder
piotr@vmgitlab:~/git$ git clone --bare project project.git #project.git is ready to be moved to the server
Create a repository/project directory on the server. Need to be own by git:git user and group.
sudo -u git mkdir /var/opt/gitlab/git-data/repositories/your_gitlab_user
Assuming that /var/opt/gitlab/git-data/repositories/ (GitLab Omnibus) exists on that server, you can set up your new repository by copying your bare repository over:
local user scp -r project.git piotr@vmgitlab.home:project.git #copy repo to the server sudo mv ~/project.git /var/opt/gitlab/git-data/repositories/piotrek #move to the right GitLab repo store path
Default remote repo path used by GitLab Omnibus installation
git:x:998:998::/var/opt/gitlab:/bin/sh git user home path GitLab repos store | / /var/opt/gitlab/git-data/repositories/piotrek | this is a namespace equals a username or group project name all projects .git folders are stored here
sudo chown -R git:git /var/opt/gitlab/git-data/repositories/piotrek/ #each file need to be owned by git:git
Add repo to GitLab, below command for Omnibus Installation. It will search, add and process all git repositories.
piotr@vmgitlab:~$ sudo gitlab-rake gitlab:import:repos
Processing piotrek/project.git
* Created gittest (piotrek/project.git) <- repo has been added
Processing piotrek/ansible.git
* ansible (piotrek/ansible.git) exists
Processing piotrek/ansible.wiki.git
* Skipping wiki repo
You will find the new project on the web interface of GitLab where it can be cloned from.
Stage files
Add files to a staging area for changes to be tracked
git add aaa.txt git add . # prior to 2.0 - . means current directory and all subdirectories git add -A # prior to 2.0 - add all files, even file deletions are included git add '*.txt' # add files using a wildcard, notice that quotes are required git reset <filename> # remove a file from staging area, the file and its changes will not be tracked # New behaviour in Git 2.0 # To add content for the whole tree, run: git add --all :/ # -A # To restrict the command to the current directory, run: git add --all . # -A
Commit changes
The files are in the Staging Area, are not in a repository yet. To store our staged changes we run:
git commit -m "Description of what we changed"
Good Commit messages
# Format of the commit message <type>(<scope>): <subject> <body> <footer> # Example fix(middleware): ensure Range headers adhere more closely to RFC 2616 Add one new dependency, use `range-parser` (Express dependency) to compute range. It is more well-tested in the wild. Fixes #2310
Allowed <type> values: #
- feat (new feature for the user, not a new feature for build script)
- fix (bug fix for the user, not a fix to a build script)
- docs (changes to the documentation)
- style (formatting, missing semi colons, etc; no production code change)
- refactor (refactoring production code, eg. renaming a variable)
- test (adding missing tests, refactoring tests; no production code change)
- chore (updating grunt tasks etc; no production code change)
Example <scope> values: init, runner, watcher, config, web-server, proxy
Add local repository to remote GitHub server
creates repo on remote GitHub name server and repository git remote add origin https://vmgitlab.home/yourgitspace/gitrepo.git #uses https connection git remote add origin git@vmgitlab.home:piotrek/ansible.git #SSH connection; this need to be created in first place via Gitlab, create/add new project #Gitlab Omnibus server path for repos is /var/opt/gitlab/git-data/repositories/piotrek #containing relevant directories: ansible.git and ansible.wiki.git git remote -v #shows remote 'origin' repo path origin git@vmgitlab.home:piotrek/ansible.git (fetch) origin git@vmgitlab.home:piotrek/ansible.git (push)
Push commits (code) to a remote server
$ git remote -v #shows remote repository that locally is called 'origin' origin git@gitlab.com:pio2pio/python27-la.git (fetch) origin git@gitlab.com:pio2pio/python27-la.git (push)
When we want to push our code the first time, we need to configure the upstream server (think as: a source of the sources)
git push --set-upstream origin master #push commits to origin remote repository of the master branch git push -u origin master #-u is short version of --set-upstream
Next time using git push
will be sufficient.
Pull and rebase
It happens sometimes that you both work on the same branch and your colleague has committed a code a head of you then you committed just after him. So you ended up with the code that cannot be pushed up as your branch is behind the remote repo commits. If you try to pull, the newest changes will override your code. In this situation you can 'rebase'
git pull --rebase
What it does, is pulling the new code and applies your code on the top of it and creates a new commit. Your previous local commit will be deleted.
- Interactive rebase, squash local commits and
push --force
to your remote branch rewriting history
git log --oneline 88c7520 (HEAD -> init, origin/init, init_bak) env acfce2f deb2 f07c09b deb2 342b8fc deb2 faaf152 deb2 8864ec2 deb2 5b6364f deb2 baf74f7 deb2 811c580 deb 7015b0c deb 6f4b90f deb 49429bc deb 9c096a1 deb1 ed675c3 new backend file 89c262f backend file dc27559 param interpolation 7707b98 pref lib dafd765 region removed from pipe # <- 18 commits to rebase(squash) into this 18th commit ccc871f libs prefix 380a40c init 1a0c2ee (origin/master, master) init # Decide, how many commits you wish to rebase, eg. 18 git rebase HEAD~18 --interactive pick dafd765 region removed from pipe pick 7707b98 pref lib pick dc27559 param interpolation pick 89c262f backend file pick ed675c3 new backend file pick 9c096a1 deb1 pick 49429bc deb pick 6f4b90f deb pick 7015b0c deb pick 811c580 deb pick baf74f7 deb2 pick 5b6364f deb2 pick 8864ec2 deb2 pick faaf152 deb2 pick 342b8fc deb2 pick f07c09b deb2 pick acfce2f deb2 pick 88c7520 env # Rebase ccc871f..88c7520 onto f07c09b (18 commands) # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup <commit> = like "squash", but discard this commit's log message # x, exec <command> = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop <commit> = remove commit # l, label <label> = label current HEAD with a name # t, reset <label> = reset HEAD to a label # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] # . create a merge commit using the original merge commit's # . message (or the oneline, if no original merge commit was # . specified). Use -c <commit> to reword the commit message. # These lines can be re-ordered; they are executed from top to bottom. # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # Note that empty commits are commented out
So now, I want to squash deleting commit messages, thus I use 'f, fixup'. Note, teh commits are in the reversed order, most recent is at the bottom. I want to melt up all commits from most recent into one of the first ones pick dafd765 region removed from pipe
. Thus changing pick
to fixup
as below, then save to apply.
pick dafd765 region removed from pipe # <- pick the oldest commit you wish to rebase(squash) on top of it fixup 7707b98 pref lib fixup dc27559 param interpolation fixup 89c262f backend file fixup ed675c3 new backend file fixup 9c096a1 deb1 fixup 49429bc deb fixup 6f4b90f deb fixup 7015b0c deb fixup 811c580 deb fixup baf74f7 deb2 fixup 5b6364f deb2 fixup 8864ec2 deb2 fixup faaf152 deb2 fixup 342b8fc deb2 fixup f07c09b deb2 fixup acfce2f deb2 fixup 88c7520 env
When completed you should see similar
git rebase HEAD~18 --interactive Successfully rebased and updated refs/heads/init. # Notice change in the history log git log --oneline f7ec963 (HEAD -> init) region removed from pipe ccc871f libs prefix 380a40c init 1a0c2ee (origin/master, master) init
Push force, to avoid Updates were rejected because the tip of your current branch is behind
git push --force ... + 88c7520...f7ec963 init -> init (forced update)
Updating a local repo from a Remote Repository
Fetch changes from the remote repository
git fetch doesn’t touch a working tree at all but updates tracking system allowing you to decide what you want to do next
git fetch #download changes to local repo from remote 'origin/master' branch git diff master origin/master #compare local 'master' branch with remote 'origin/master' branch git merge origin/master #while working on 'master' (after a git checkout master) merge in the changes that you’ve just got from 'origin'
Pull changes and automagicly merge
git pull origin master #from the 'origin' remote repository its master branch git stash # to stash your not committed changes before pull git stash apply # to re-apply your changes after your pull
Preview changes of your staged files
git diff --staged
Working with branches
Git has 2 types of branches:
Local branches -what you see when you type git branch, e.g. to use an abbreviated example I have here:
$ git branch debian server * master
Remote-tracking branches -what you see when you type git branch -r, e.g.:
$ git branch -r cognac/master origin/albert origin/ant
The names of tracking branches are made up of the name of a "remote" (e.g. origin, cognac) followed by "/" and then the name of a branch in that remote repository.
These are stored locally:
- .git/refs/heads/ [for local branches]
- .git/refs/remotes/ [for tracking branches]
Recovering files (from staging area, HEAD, or other)
Retrieve file from the staging area
git checkout directory/file.txt
Retrieve a file from the latest commit
git checkout HEAD -- directory/file.txt
Retrieve a file from a previous commit
git checkout HEAD^ -- directory/file.txt #or git checkout 13bd00 -- directory/file.txt
It’s important to note that this stages the file, too!
permanently remove few commits from remote branch
You git reset --hard
your local branch to remove changes from working tree and index, and you git push --force
your revised local branch to the remote. (other solution here, involving deleting the remote branch, and re-pushing it)
Undoing changes
In a local branch
Undo a merge that hasn't been pushed yet
Make your local master branch look identical to origin/master. It is possible that origin/master may be ahead of your local master just previous to the merge by some commits, in that case this might not give the desired results
(master)$ git reset --hard origin/master
The ref ORIG_HEAD will point to the original commit from before the merge, --merge doesn't touch uncommitted changes
(master)$ git reset --merge ORIG_HEAD
With newer Git versions, if you have not committed the merge yet and you have a merge conflict:
git merge --abort
Undo a failed merge
Option 1 - git clean
# Show files that git would purge if you weren't doing a dry run: git clean -ndx # Purge all those files from your working tree: git clean -fdx # also removes all files that aren't being tracked, including ignored files # | -f force # | -n dry run # | -d recurse also into untracked directories # | -x don’t use the standard ignore rules, allows removing all untracked files
Option 2 - Abort git merge
git merge --abort # | --abort Abort the current conflict resolution process, and try to reconstruct the pre-merge state.
Resetting branch pointer without changing files
This command will move the current branch pointer back by one commit
git reset HEAD~1
You can check the result by running git show which will give you the details of the most recent commit on the branch. Running it before and after resetting the branch pointer should show how it moved to the previous commit. Note that the files stay in the state of your undone commit and will therefore be marked as modified (or added, deleted) again. I think of this command as moving changes from the last commit back to the staging area.
Resetting branch pointer and files
Set back the current branch by one commit and undo all changes from that commit
git reset --hard HEAD~1
This will completely wipe out the most recent commit from the current branch. I am not sure if the actual commit data is also deleted, but it will definitely no longer be part of the current branch and your repository should look exactly like one commit ago.
Amending the most recent commit
There is a flag for the commit command itself that allows you to pack changes on top of the most recent one. Instead of creating a new commit, Git will replace the most recent one. The new comment message will override the previous one.
git commit --amend -m "New commit"
Undo Last Commit with git revert
In order to revert the last Git commit, use the “git revert” and specify the commit to be reverted which is “HEAD” for the last commit of your history.
The “git revert” command is slightly different from the “git reset” command because it will record a new commit with the changes introduced by reverting the last commit.
git revert HEAD
Remove files
git rm '*.txt' git rm -r folder_of_cats #remove folders recursively git commit -am "Delete stuff" #-a auto removes deleted files within commit
Merge other branch with the current active branch
You always merging into the current 'checked out branch'. So, if you currently working in 'master-vpc' branch
git merge aws-subnet
will write code from 'aws-subnet' branch to your 'master-vpc' branch.
Preview changes before merging
Current branch is 'master-vpc'. So to preview changes before importing/merging 'aws-subnet' branch into the current use this command:
git diff aws-subnet
git worktree
Check out a different branch in your repository, in a seperate directory. It looks like and is used like a regular git repository, but it allows you to have multiple active working trees in the same repository at the same time.
- Add worktree
git worktree add ../another-directory
This will create a new worktree in another-directory, which is a sibling of the current directory. At the same time it will also create a new branch with the same name as the directory.
- Specifying branch name
git worktree add -b new-branch ../another-directory git worktree add ../another-directory existing-branch|commit
Remove the worktree
git worktree remove ../another-directory
All your commited changes will be present in the main repository, as if you had done the work there.
Git diff - preview a file changes
Compare 2 branches
The diff command does not work on symbolic links, you need you point to the hard file.
Changes between commits, ^ - last commit, ^^ - 2 commits and so on
git diff HEAD^ HEAD main.c
git diff (working directory and staging) git diff --cached (staging and repo) git diff HEAD (working dir and repo) git diff 225bb (?) git diff HEAD 225bb (?) git diff MY-PROGS/TEST.PROG.A (?)
Navigate with Space (next page), Enter (next line), B (previous page), Q (quit)
Show all historical commits against the file
git log -- [filename] git log --follow -p -- [filename] #show content/diff
Show the file content/diff commits history
gitk [filename]
Preview a file between branches
git diff mybranch branch_to_compare -- folder/file_to_compare.txt
Show a file form a different branch
git show origin/branchName:path/to/file
Search through commits logs
git log --grep="string" #will look for a string in every commit comment git log --graph --decorate #will show branches as a tree
Resolve git conflicts
When you get into conflicts your index is locked so you cannot change the current branch until cnflicts are resolved.
The files with conflicts will be changed and markers added on:
<<<<<<< HEAD:file.txt #this is a delimiter Hello world #this top part shows current branch file content ======= #this is delimiter Goodbye #bottom part whows content of the file from master branch >>>>>>> master #this is delimiter to delete
You can edit the file keep or remove the offending lines to resolve the conflict. Then remember to add the file to commit using git add folder/file.txt
You can also setup vimdiff (multi-window comparision) as the default git merge tool, plus disable a prompt when running git mergetool
git config merge.tool vimdiff git config merge.conflictstyle diff3 git config mergetool.prompt false
Set all branches to be tracked
#!/bin/bash for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master `; do git branch --track ${branch#remotes/origin/} $branch done
Download/fetch all tracking branches
git fetch
List all your repositories
Replace $GHUB_TOKEN with authorization token.
alias ghrepos="curl -s -i -H 'Authorization: token $GHUB_TOKEN' \> https://api.github.com/user/repos | grep -o 'https://github.com[^\"]*.git'"
Keep empty directory in a repository
Although it's not supported by design to track an empty directories you can add empty .gitkeep
file more as convention but this can be any empty file.
Tags
Add tag in your local branch
$ git tag version1 #add $ git tag #list all tags $ git push version1 #push only a particular tag $ git push --tags #push all tags to remote
Delete a tag locally then remove from remote repo
git tag --delete version1 git push origin :refs/tags/version1