I do not know in which programming language you write, but I am sure that you are using Git during development. There are more and more tools to support development, but even the smallest test project, I always start with the git init
command. And during the working day I type an average of 80 more teams, referring to this version control system.
I spent a lot of nerves when I began to relearn a ten-finger printing method. In the end, this was the most correct decision to improve personal workflow. The next most important optimizations are the in-depth development of the Gita.
Habr has written many articles about Gita, but they do not go further than the official documentation, and the authors offer to simplify the work with self-written crutches. I am sure that it is necessary to study the Git with concrete examples of tasks, and to increase the efficiency of working with it using standardized tools.
Have you already mastered the Gita gentleman's set and are ready to move on? There are 2 ways:
Dedicate the experiments described in the article for a couple of hours today, and save, by approximate calculations, half a year of working life.
Welcome under the cut!
Among the developers, the standard alternative to Bash
is Zsh
, an advanced software shell that supports fine tuning. And among Zsh
users, the standard is the use of Oh My Zsh
- a set of ready-made settings for Zsh
. Thus, having established this kit, we will get out of the box a set of hacks that the community has been collecting and working out for us.
It is very important to note that Zsh
is for Linux , and for Mac , and even for Windows .
Installing Zsh
and Oh My Zsh
Install Zsh
and Oh My Zsh
as instructed by one command:
# macOS brew install zsh zsh-completions && sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" # Ubuntu, Debian, ... apt install zsh && sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
Since the task is to optimize the interaction with Git , add a couple of plugins to Zsh
. Open the ~/.zshrc
and add plugins
to the list:
plugins=(git gitfast)
Total:
git
- a set of aliases and auxiliary functions;gitfast
- improved autocompletion for gita.tig
And the final touch is installing the tig
console utility:
# macOS brew install tig # Ubuntu, Debian, ... # https://jonas.imtqy.com/tig/INSTALL.html
About her talk further.
It is best to deal with Git with examples of solving specific problems. Next, we consider the tasks from daily practice and options for their convenient solution. To do this, consider a repository with text files.
In yellow blocks, the main alias is specified for solving the problem from the section. Learn only it, and leave everything else for general development.
Let's start with the most basic things. We did some work and now let's see what happens in the working directory:
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: e.md Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: b.md Untracked files: (use "git add <file>..." to include in what will be committed) d.md
The current state of all files is described in great detail, additional guidance is given to the action. Very useful at the beginning of the use of the Gita, but for the daily work a lot of superfluous. Let's lower the noise level with additional keys:
$ git status -sb ## master M b.md A e.md ?? d.md
Yeah, we are in the master
branch, changed the b.md
file ( M-odified
) and created two files, adding the first one to the Gita index ( A-dded
), and leaving the second one out of the index ( ??
). Briefly and clearly.
It remains to optimize the infinite input of this command by the alias “ g it s tatus with b ranch” :
Show abbreviated working directory status
$ gsb # git status -sb
We continue.
Of course, you can create commits. But let's try to optimize the solution to this simple task. Add all changes to the index with the alias “ g it a dd a ll” :
$ gaa # git add --all
We check that the index got exactly what we need with the help of the alias “ g it d iff ca ched” :
$ gdca # git diff --cached diff --git a/b.md b/b.md index 698d533..cf20072 100644 --- a/b.md +++ b/b.md @@ -1,3 +1,3 @@ # Beta -Next step. +Next step really hard. diff --git a/d.md b/d.md new file mode 100644 index 0000000..9e3752e --- /dev/null +++ b/d.md @@ -0,0 +1,3 @@ +# Delta + +Body of article.
Hm, in one kommit the changes solving the only task have to fall. Here, the changes of both files are not related to each other. Let's exclude the d.md
file from the index by the alias “ g it r eset u ndo” :
$ gru d.md # git reset -- d.md
And create a commit with the alias “ g it c ommit” :
$ gc # git commit
We write the name of the commit and save. d.md
create another commit for the d.md
file d.md
more familiar command using the alias “ g it c ommit m e s sa g e” :
$ gaa # $ gcmsg "Add new file" # git commit -m "Add new file"
And we can ...
... commit modified files from the index with one command:
$ gcam "Add changes" # git commit -a -m "Add changes"
... look at changes by words instead of lines (very useful when working with text):
$ gdw # git diff --word-diff
... add files in parts (very useful when you need to add only a part of changes from a file to a commit):
$ gapa # git add --patch
... add to the index only files already under the supervision of the Gita:
$ gau # git add --update
Total:
Add to index / create commit
$ ga # git add $ gc # git commit
The name of the last commit does not explain the changes we made. Let's reformulate:
$ gc! # git commit -v --amend
And in the opened text editor, let's call it more understandable: "Add Delta article"
. I'm sure you never use the -v
, although when editing a commit description, it shows all the changes that were made, which helps you get your bearings.
And we can ...
... make changes to the commit file, but do not touch the description:
$ gcn! # git commit -v --no-edit --amend
... make all changes to the files immediately to the commit, without first adding to the index:
$ gca! # git commit -v -a --amend
... combine the two previous commands:
$ gcan! # git commit -v -a --no-edit --amend
Well, it is important to note again that instead of typing the full, regularly used git commit -v --amend
, we write only three characters:
Change last commit
')
$ gc! # git commit -v --amend
Create a new branch from the current alias “ g it with heckout b ranch” :
$ gcb erlang # git checkout --branch erlang
Although no, it is better to write an article about the more modern language of the Elixir with the alias “ g it b ranch with the key m ove” (renaming in the Gita is done via move
):
$ gb -m elixir # git branch -m elixir
Here it would be logical to use the gbmv
alias, but, unfortunately, it has not yet been invented. A good option for contributing.
We make changes to the repository and create a commit as we already know:
$ echo "# — ." > e.md $ gaa && gcmsg "Add article about Elixir"
And remember:
Create a new thread
$ gcb # git checkout --branch
Now add our new article about Elixir to master
. First, we switch to the main branch with the alias “ g it c heckout m aster” :
$ gcm # git checkout master
No seriously. One of the most frequently used commands in three easy to remember characters. Now merdzhim changes by alias " g it m erge" :
$ gm elixir # git merge elixir
Oops, and in master
someone has already managed to make their changes. And instead of the beautiful linear history that we adopted in the project, a hated merge commit
Merge branches
$ gm # git merge
Nothing wrong! You just need to delete the last commit and try to merge the changes again “ g it r eset hh ard” :
Delete last commit
$ grhh HEAD~ # git reset --hard HEAD~
The standard checkout – rebase – merge
sequence of actions for preparing a linear change history is performed by the following sequence of aliases:
gco elixir # git checkout elixir grbm # git rebase master gcm # git checkout master gm elixir # git merge elixir
All of them are so often used that they fly away from the fingers, and while doing such operations, there is no need to think about which set of letters you need to type. And do not forget that in Zsh
you can add the names of branches with the Tab
key.
Rebase
$ grb # git rebase
First add origin
alias “ g it r emote a dd” :
$ gra origin git@github.com/... # git remote add origin git@github.com/...
And then we send the changes directly to the current repository branch ( “gg” - double g
at the beginning of the command indicates that the action is executed to the current branch):
$ ggpush # git push origin git_current_branch
You also can...
... send changes to the server with the upstream
installation with the alias “ g it p ush s et up stream” :
$ gpsup # git push --set-upstream origin $(git_current_branch)
Send changes to the server
$ gp # git push
Work is in full swing. We managed to add a new article f.md
to master
, and our colleagues changed the article a.md
and sent this change to the server. This situation is also solved very simply:
$ gup # git pull --rebase
Then you can safely send changes to the server. The conflict is settled.
Get changes from server
$ gl # git pull
So, we have successfully poured several branches into master
, including the elixir
branch from the previous example. We don't need them anymore. You can delete it with the alias “ g it b ranch d elete a nother” :
$ gbda # git branch --no-color --merged | command grep -vE "^(\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d
Very beautiful and cunning team. I usually forget to clean up the lost branches and this elegant team is a real salvation. If you do not want to use an alias, just copy the full version of the command into your notes, and execute it as needed.
Work on a new article h.md
about Haskell is in full swing. Written half and need to get feedback from colleagues. Without thinking twice , we type “ g it w w ork i n p rogress” :
$ gwip # git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify -m "--wip-- [skip ci]"
And then a commit is created with the name Work in Progress
, which skips CI and deletes the "extra" files. We send the thread to the server, talk about it to a colleague and wait for a review.
Then this commit can be canceled and the files returned to their original state:
$ gunwip # git log -n 1 | grep -q -c "\-\-wip\-\-" && git reset HEAD~1
And you can check if there are WIP
commits in your branch with the command:
$ work_in_progress
The gwip
command is a fairly reliable analogue of stash
when you need to switch to the next branch. But in Zsh
there are a lot of aliases for the stash
itself.
Add Temporary Commit / Reset Temporary Commit$ gwip $ gunwip
Be careful with this command. You can hide the files and then delete them with a careless action for good, since there is a reflog
in which you can try to find lost work.
Let's hide the files we are working on with the alias “ g it st ash a ll” :
$ gsta # git stash save
And then we will return them back with the alias “ g it st ash p op” :
$ gstp # git stash pop
Or the safer method “ g it st ash a ll pply” :
$ gstaa # git stash apply
You also can ...
... see exactly what we hid:
gsts # git stash show --text
... use abbreviations for related commands:
gstc # git stash clear gstd # git stash drop gstl # git stash list
Hide Changes / Get Changes$ gsta $ gstaa
The git-bisect
tool that repeatedly saved my life also has its aliases. We start with the launch of the procedure “binary error search” by the alias “ g it b i s ect s tart” :
$ gbss # git bisect start
We note that the current, last in a branch, commit contains an error, with the alias “ g it b i s ect b ad” :
$ gbsb # git bisect bad
Now we mark the commit that guarantees us the working state of the application “ g it b i s ect g ood” :
$ gbsg HEAD~20 # git bisect good HEAD~20
And now it remains to continue to answer the questions of Gita with phrases gbsb
or gbsg
, and after finding the culprit, reset the procedure:
$ gbsr # git bisect reset
And I do write these abbreviations when using this tool.
Find a commit with an error
$ gbss # git bisect start $ gbsb # git bisect bad $ gbsg # git bisect good $ gbsr # git bisect reset
Even with a high percentage of code coverage tests, no one is immune from the situation when the application falls and kindly points to a specific line with an error. Or, for example, in our case we want to know who made the mistake in the second line of the a.md
file. To do this, run the command:
$ gbl a.md -L 2 # git blame -b -w a.md -L 2
You see, Oh My Zsh
contributors made an alias not just to the git blame
command, but added keys to it that simplify the search for the instigator himself.
To view the list of commits, use the git log
command with additional output formatting keys. Usually this command, together with the keys, is entered into Gita custom aliases. You and I were luckier, we already have a ready alias out of the box: glog
. And if you installed the tig
utility on the advice from the beginning of the article, then you are the absolute champion.
Now, to study the history of commits in the console in a very convenient way, you need to type the word git
vice versa:
$ tig
The utility also gives a couple of useful additions that are not in the Gita out of the box.
First, the command to search through the contents of the story:
$ tig grep
Secondly, viewing the list of all sources, branches, tags along with their history:
$ tig refs
Thirdly, maybe you will find something interesting for yourself:
$ tig --help
git reset --hard
You worked on the elixir
branch all day:
$ glog * 17cb385 (HEAD -> elixir) Refine Elixir article * c14b4dc Add article about Elixir * db84d54 (master) Initial commit
And in the end, everything was accidentally deleted:
$ grhh HEAD~2 HEAD is now at db84d54 Initial commit
No need to panic. The most important rule - stop executing any commands in the gita and exhale . All actions with a local repository are recorded in a special log - reflog
. From it, you can get the hash of the desired commit and restore it in the working tree.
Let's take a look at the reflog, but not in the usual way through git reflog
, but more interesting with detailed decoding of the records:
$ glg -g
Find the hash of the required 17cb385
commit and restore it:
# $ gcb elixir-recover 17cb385 # $ gbd elixir # $ gb -m elixir
Here again the reflog comes to the rescue. Find the hash of the original 17cb385
commit, if we cancel the commit immediately, then instead of searching for the hash, we can use the quick link to it HEAD@{1}
. Then we do a soft reset, the index is not reset.
# $ grh --soft HEAD@{1} # git reset -soft # $ gcmsg "Commit description"
Sometimes you start working on features, but its release is postponed indefinitely. You make a commit and switch to other tasks. Together with the team you make a lot of changes to the master and after a while you return to the branch with features. You try to make a rebase, but he offers to sort out conflicts in a dozen commits. You can try to solve them all or make it easier.
Let's take a look at the example of a feature branch called elixir
:
# master $ gcm # git checkout master # $ gcb elixir-new # git checkout --branch elixir-new # $ gcp elixir@{0} # git cherry-pick elixir@{0}
So, instead of trying to update a branch, we take one single commit without any problems.
To delete important data from the repository, I have saved the following snippet:
$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <path-to-your-file>' --prune-empty --tag-name-filter cat -- --all && git push origin --force --all
Running this command will break your stash
. Before its execution it is recommended to get all the hidden changes. Read more about this admission link .
When executing some commands that are waiting for the name of the branch, we can pass a hyphen -
as a link to the branch with which we came. Especially good to use this trick for checkout:
$ gco - # git checkout - $ gm - # git merge - $ grb - # git rebase -
.gitignore
Another frequent failure is that it is too late to add unwanted files or directories to .gitignore
. In order to clean them from the repository ( and delete from the disk ) there are already ready keys for the git clean
command:
$ gclean -X # git clean -Xfd
Be careful!
How to perebdet read on.
--dry-run
?The key --dry-run
needed just as a caution when deleting and updating tasks. For example, the previous section describes how to delete everything that is specified in the .gitignore
file. It is better to show caution and use the --dry-run
key, --dry-run
list of all files to be deleted, and only then execute the command without --dry-run
.
The article shows the point to optimize the work programmer. Remember 10-20 mnemonic abbreviations is easy, it is almost impossible to forget the original commands. The aliases are standardized, so when you move the whole team to Zsh
+ Oh My Zsh
, you can work with the same speed and comfort, even with pair programming.
I suggest the following options:
git --help
or ghh
.Some aliases are not trivial, but they are very useful in practice. Many of the aliases presented are not just abbreviations, but small functions that further optimize the work. Using the Git has become more pleasant, the quality of commits has increased.
I hope the material turned out to be useful, and you were able to learn something new for yourself. Or maybe they have already begun to actively introduce a new approach. Good luck!
Source: https://habr.com/ru/post/420529/
All Articles