📜 ⬆️ ⬇️

Advanced use of Gita or how to retire half a year earlier?


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.


Who would benefit from this article?


Have you already mastered the Gita gentleman's set and are ready to move on? There are 2 ways:


  1. Master abbreviated commands - aliases. They are almost always composed mnemonically and easily remembered. Forgetting the original commands is problematic, I easily type them when it is required. Plus, I don’t go astray when checking something in Gita in the process of writing code.
  2. Learn about additional flags for teams, as well as their association with each other. I understand that someone hates cuts. For you, too, there is an interesting material in the article - how to increase the utility and convenience of the output of commands, as well as how to solve not the most trivial, but often encountered in practice tasks .

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!


Training


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:



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.


Git in practice


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.

Check the status of the working directory


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 


Create a commit


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 


Fix 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 


We start working on a new feature


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 


Merge changes


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 


Delete the last commit


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~ 


Resolve conflicts


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 


Send changes to the server


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 


We receive changes from the server


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 


Remove merged branches


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.


Create a temporary commit


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 


Hide changes


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 


We are looking for a bug


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 


We are looking for the instigator of mayhem


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.


Bonus


View a list of commits


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 

Accidentally made 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 

Accidentally, instead of creating a new commit, made changes to the previous one.


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" 

The branch is too old


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.


Deletion of important data from the repository


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 .


Appeal to the previous branch


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 - 

Delete all files marked in .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.


Why do many teams need the --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 .


Conclusion


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.


Where to go next?


I suggest the following options:


  1. Finally, figure out how the gith is arranged inside . It helps to understand what you are doing and why what you want to do is impossible.
  2. Do not be lazy to once again look into the documentation for the commands: git --help or ghh .
  3. See the full list of aliases by reference . Trying to memorize them all is insane, but using the list as a collection of interesting teams and keys to them is a good idea.

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