DEV Community

Cover image for Git Concepts I Wish I Knew Years Ago

Git Concepts I Wish I Knew Years Ago

Gabriel Abud on June 29, 2020

The most used technology by developers is not Javascript. It's not Python or HTML. It hardly even gets mentioned in interviews or listed as a pre...
Collapse
 
sqlrob profile image
Robert Myers

Just a warning on branches that I've learned the hard way.

You have branch foo/bar. Do NOT EVER create a branch foo.

This may have changed since it's an internal git detail, but in the first case, foo would be a directory. In the second, foo is a file. Something can not be both a file and directory at the same time, so you're asking for a world of hurt.

Collapse
 
bloodgain profile image
Cliff • Edited

I would recommend foo-bar style naming instead. Tab completion works just as well, and there's no unnecessary directory structure created in refs. It also completely avoids this error.

EDIT: I found one downside to this. If you like to set all your primary branches to be the default output for git show-branch and not show topic branches, you have to use subdirectories for your topic branches. I don't use show-branch much at this time, but this could matter to someone who does. Reference: git-scm.com/docs/git-show-branch#_...

Collapse
 
g_abud profile image
Gabriel Abud

Interesting, I've never run into this situation but it makes sense. Will keep this in mind.

Collapse
 
markhu profile image
Mark

Slight tweak: replace "Add a name field to the checkout form" with "Add name field to checkout form" --removing the articles saves 15% which can help keep you under the 50-character recommended commit message first-line length.

Collapse
 
ilanvivanco profile image
Ilán Vivanco

Very neat summary of git commands! Nice job.

Totally agree that rebase is the most confusing/frustrating thing to learn about git,. But once you practice a bit with on a test repo (I wouldn't dare to do it on a real project 😂) it's a very useful command to work with many branches.

Collapse
 
dimaip profile image
Dmitri Pisarev 🇷🇺

git rerere to make rebase workflow a little less painful?

Collapse
 
philipoakley profile image
Philip Oakley

Though git rerere could do with a bit more user facing documentation ;-)

Collapse
 
xamtheone profile image
xamtheone

Modern git has replacements for the awkwardness of git checkout doing two different jobs:

  • git switch to switch branches
  • git restore to restore a file to its original state
Collapse
 
bloodgain profile image
Cliff

Never push -f unless you're the repo manager repairing some terrible mistake that will require everyone to re-clone the repository. I always set my upstream/shared repository to fast-forward pushes only. I call it the "No Jedi Rule" -- force pushing not allowed!

Collapse
 
bernardwiesner profile image
Bernard Wiesner

Personally I often push force just to keep my commits clean, otherwise have an ugly history like 'bug fix', 'another bug fix', especially when working on new features that require many changes. Also nice to rebase from parent branch to avoid having all the merge commits.

Of course only push force when working alone on your branch.

Collapse
 
bloodgain profile image
Cliff

Oh, definitely, when working alone, I break this rule all the time. It's really just part of the greater rule to never rewrite shared history. You can rewrite your own history all you want, though.

I think rebase is under-utilized, too. If I'm working directly on a shared branch, I almost never do a pull. I always fetch and rebase to keep the history clean. It actually does more than that, though, it keeps the first parent path consistent, which makes the log much easier to read and the graph much easier to trace. I'm also a fan of using rebase -i to squash feature branches into a single commit.

Collapse
 
mungojam profile image
Mark Adamson

I would add git status as a really handy command because it tends to teach you about other commands. For example, it will tell you the git rebase abort command when you are part way through a rebase

Collapse
 
g_abud profile image
Gabriel Abud

Added!

git status is a pretty beginner level command so I didn't include it at first but you're right that it does much more than we think and it's possibly one of the best learning tools in really internalizing git.

Collapse
 
darrylhodgins profile image
Darryl Hodgins

Careful now, git push -f requires that everyone on the team have as good an understanding of git as you do, particularly when dealing with shared branches like master.

We generally don't ever want to see rewriting history on shared branches, because once any other team member grabs the latest (fetch/pull), they're going to have to deal with branches that have diverged. It goes from being a problem for one dev, to being a problem for the whole team.

When others are involved, it's a much better practice to resolve the issue as a fast-forward or merge commit, then tack that on to the HEAD of the remote branch.

Collapse
 
g_abud profile image
Gabriel Abud

Agreed, I will probably add that as a warning. Good suggestion.

Collapse
 
goodevilgenius profile image
Dan Jones • Edited

git specific aliases are generally better done as git aliases, rather than shell aliases. This way, they only apply when working with git, they don't need to be added to your shell rc, and also, if you decide to switch shells, they'll still work.

E.g., instead of

alias gbda='git branch --no-color --merged | command grep -vE "^(\+|\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d'
Enter fullscreen mode Exit fullscreen mode

Do:

git config --global alias.bda '!git branch --no-color --merged | command grep -vE "^(\+|\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d'
Enter fullscreen mode Exit fullscreen mode

Now, instead of gbda, you run git bda.

Collapse
 
nerolauda profile image
nerolauda

There is a couple of typos.
Your last sentence should be:
"Now, instead of gbda, you run git bda"

Collapse
 
goodevilgenius profile image
Dan Jones

Thanks! I've corrected it.

Collapse
 
rafo profile image
Rafael Osipov

I would suggest the following approach to name branches. Assume your team is working on a project with name "Acme Project".

Assign an abbreviation to the project, let say this abbreviation is "ACM".

Then assign tasks in JIRA (or another tracking system) and assign unique number to every task. For example:

ACM-1: Implement "About" dialog.
ACM-2: Fix printing issues.
ACM-291: Refactor "Customer" class and reduce methods complexity and size.

Then assign these tasks to developers.

Upon getting a task, let say ACM-2, a developer creates a local branch with the same name: ACM-2, and works on it. Then it pushes this branch to the remote and makes Pull Request to the master. As the pull request approved and merged, the remote branch ACM-2 is being deleted and the relevant task is being marked as Done.

This approach simplifies communication and task/branch referencing inside of team and I recommend it.

Collapse
 
bimlas profile image
bimlas

I also follow this kind of branch name and wrote a prepare-commit-msg hook for it, which (among other things) places the issue tracker URL in the commit message based on the branch name.

gitlab.com/bimlas/home/-/blob/f826...

Collapse
 
g_abud profile image
Gabriel Abud

Yeah agreed that this makes sense if you use Jira. Not everyone uses project management tools like Jira though.

Collapse
 
devimposter1 profile image
devimposter

Maybe not Jira but they should be using something!!! Even working alone it's worth using these tools. Kanban, etc. You can do it free with a Gitlab or Azure DevOps account, so why not?

Thread Thread
 
jaymcconnon profile image
James McConnon

Agreed, i just managed a solo project with gitkraken boards and github issues and i am so glad i did. Kept me on task and eventually was the basis for my generated changelog.

Collapse
 
jnareb profile image
Jakub Narębski

A few things. First, do not use git branch for scripting, as it is meant to be user-facing command which output format can change and which is subject to config, but git for-each-ref explicitly meant for scripting.

Second, instead of git push --force, use safer alternative of git push --force-with-lease which would overwrite only your changes.

Third, git restore might be easier to use than git reset.

Collapse
 
eltonvs profile image
Elton Viana

Hey Gabriel, nice article!

I would add another command in order to have a less destructive force push: git push --force-with-lease (or ggfl when using oh my zsh).
This works like a git push --force, but will not override the upstream branch if there's some modification. I usually default to this command when pushing my rebased branches.

Here's an article with a good explanation about that: blog.developer.atlassian.com/force...

Collapse
 
dearwish profile image
David Roberin

Nice article, but IMHO not deep enough. I used git command line for a long time, but then moved to visual tools.
Currently I use an awesome visual client: Fork. It really simplifies my work with git.
In the past I used GitKraken which is similar to Fork, but has more features.

Collapse
 
devimposter1 profile image
devimposter • Edited

Fork looks cool, will try that out. Currently using Gitkraken and built in JetBrains tools but I like the way this looks. I used to use command line too but when you have 20 plus repos it's so much easier to treat these like projects inside a GUI. Thanks for mentioning this. Edit: Dang no Linux client... I need all platforms.

Collapse
 
nerolauda profile image
nerolauda

For Windows there is atlassian sourcetree.
BTW many IDE have git plugins or capabilities to easy your work.

Collapse
 
dearwish profile image
David Roberin

I agree, thanks for mentioning. I used Atlassian SourceTree before, but IMHO it is not such intuitive as Fork or GitKraken.
Regarding IDEs, I sometimes use the integration of WebStorm and InelliJ Idea, but still prefer the tools I mentioned.

Collapse
 
philipoakley profile image
Philip Oakley

The git diff with the two dots end points A..B should be deprecated as it doesn't actually follow the normal revision range method of being the range of revisions between ^A B.

Rather its just git diff A B, not git diff $(git merge-base A B) B which the two dot notation would normally imply

Collapse
 
tracygjg profile image
Tracy Gilmore • Edited

Hi Gabriel,

I thought you might find this an interesting read.
The Git Parable

Here is another resource I find helpful Git Explorer

Best regards

Collapse
 
developerdavi profile image
Davi Rodrigues • Edited

Nice article!

Just a little thing:

git reset --hard HEAD
This will reset your local directory to match the latest commit and discard unstaged changes

Wouldn't "unstaged" mean "uncommitted"? Because actually git reset --hard HEAD will reset everything to match the latest commit, including the staged changes. Only thing that won't be discarded is untracked files.

git reset behavior

Collapse
 
anonimoconiglio profile image
Santiago • Edited

Hi! thanks really useful!

My two cents: in .gitattributes, instead of using
package-lock.json binary
You can use
package-lock.json -diff

It will have the same result I guess, preventing git to show the differences ;)

(maybe is more correct with -diff according to the nature of the file)

Collapse
 
epsi profile image
E.R. Nurwijayadi

In CI/CD world, I think git worktree is the most instrument.

epsi-rns.gitlab.io/devops/2020/02/...

Comparing HEAD for each branches

Collapse
 
cambridgeport90 profile image
Katherine M. Moss

For a visual representation of things, and sometimes easier than having to remember commands, use something like GitKraken. I love that thing, but I also do use the command line ... sometimes. Could make a difference whether you develop for work or as a hobby.

Collapse
 
oneguycoding profile image
Steeve McCauley

Sometimes if I want to move some work from one location to another without creating a remote branch I diff into a file and then apply the file on the other end,

git diff > /var/tmp/foo.diff
rsync -av /var/tmp/foo.diff remote:/var/tmp
ssh remote
git apply /var/tmp/foo.diff

Collapse
 
webstackdev profile image
Kevin Brown

Thanks. I was hoping for more detail on commit messages. Google has a standard that is multi-line, and I've been wondering how (or if there's a convenient CLI tool) to do that on the command line using a template.

Collapse
 
stephencweiss profile image
Stephen Charles Weiss

@kevin - you might be interested in Conventional Commit coupled with tools like Git Hooks, Commitzen or Husky.

I wrote about it here stephencharlesweiss.com/blog/2020-...

Collapse
 
tejsinghrana profile image
Tej-Singh-Rana

Very helpful for beginners.

Collapse
 
mgh87 profile image
Martin Huter

Great summary, I use almost everything of it every day.

Collapse
 
athulmuralidhar profile image
Athul Muralidhar

git add --patch is one of my favourites. It adds files by showing exactlly what changed. In OMZ, you can use gapa does the same thing

Collapse
 
chrisvelevitch profile image
chrisvelevitch

You mentioned that you can see the diff's of all --staged changes and see the diff's of all unstaged changes, but you didn't mention how to show the diff's of all changes?

Collapse
 
miketalbot profile image
Mike Talbot ⭐

Thanks for this, really useful, I've used git for years - but always a "little nervously" lol. This will help me think about clearing up some of the clutter.

Collapse
 
g_abud profile image
Gabriel Abud • Edited

Yeah getting past the "nervous" stage takes a lot of time, I know exactly what you mean.

It will make you so much more confident in what you can do with a shared codebase and really changes the way you think about your code though.

Collapse
 
decadef20 profile image
Decade

Great summary. Marked.

Collapse
 
technikhil314 profile image
technikhil314

tig very handy tool on top of git. I like to use tig for viewing all branch history instead of git log. And I use zsh that gives all the git command aliases built in.

Collapse
 
g_abud profile image
Gabriel Abud

I'll have to check out tig, thanks for the recommendation.

Collapse
 
lifeiscontent profile image
Aaron Reisman

@g_abud I'd highly suggest mentioning --force-with-lease instead of -f when doing force pushes, as it'll save anyone learning from this a mountain of mistakes.

Collapse
 
davidcockerill profile image
DavidCockerill • Edited

Nicely done! I tend to use WebStorms git tools a lot so it's good to refresh my memory on native commands.

Collapse
 
mohamedebrahim96 profile image
Mohamed Ebrahim

Hi Gabriel

Could you please tell me what is the software that in this picture
Gitkraken - fork - source tree - github desktop ?

Uploading image

Collapse
 
miteshkamat27 profile image
Mitesh Kamat

Great. Thanks for sharing !!

Collapse
 
ormadont profile image
Evgeniy Pavlovich Demyanov

It's look dangerous for beginners.

Collapse
 
mbaneshi profile image
Mehdi Baneshi

great

Collapse
 
kiromousa profile image
kiro

Really cool! Thank You Gabriel!!

Collapse
 
shank136 profile image
Shashank • Edited

Just a question. Is there a way to automatically commit changes from local workstation to GitHub remote repository at a given schedule to save developer's work?

Collapse
 
nyangweso profile image
Rodgers Nyangweso

thanks for this Man, very well descriptive for a starter.