git and git-flow, part 2

edrpls
GumGum Tech Blog
Published in
21 min readMar 31, 2021

Introduction

Back in August 2019, I wrote git and git-flow, a guide. I intended it to be used internally by GumGum to familiarize new hires with the workflow we use.

It covers a wide range of topics for people new to git and git-flow; however, it doesn’t cover everything I wanted it to cover. It was easy to say, I’ll do it later, but life happens, and almost two years later, the first part gets modest monthly views and reads while I fear that someone else will notice I never got to writing the second part. Before that happens, let’s get to it.

In the second part, we will explore these topics:

  • Solving git conflicts
  • Working with Epics and Sub-Tasks
  • Fixing bugs in production
  • Avoid rebasing conflicts with --onto
  • Stashing work in progress
  • Many helpful commands

About our project

Our project is incredibly trivial but helps showcase the topics covered by this guide. It is an HTML file that has slowly grown by working on different tickets.

At the moment, our index.html looks like this:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Learning Git Flow</title>
</head>
<body>
<header>
<h1>Learning Git Flow</h1>
</header>
<section>
Coming soon!
</section>
<footer>
2019
</footer>
</body>
</html>

Solving git conflicts

Our next ticket is to update the year in the footer dynamically. It still says 2019.

To reaffirm what we learned in the first part of the guide, we will work on this on a feature branch with:

git flow feature start dynamic-footer-year

And with some internet magic, our new feature is complete:

<!DOCTYPE html>
<html>
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width" />
- <title>Learning Git Flow</title>
- </head>
- <body>
- <header>
- <h1>Learning Git Flow</h1>
- </header>
- <section>
- Coming soon!
- </section>
- <footer>
- 2019
- </footer>
- </body>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Learning Git Flow</title>
+ </head>
+ <body>
+ <header>
+ <h1>Learning Git Flow</h1>
+ </header>
+ <section>Coming soon!</section>
+ <footer>
+ <div>
+ <a
+ href="https://medium.com/gumgum-tech/git-and-git-flow-a-guide-871d46a0ebcb"
+ rel="noopener noreferrer"
+ >git and git-flow, a guide</a
+ >
+ </div>
+ <div>
+ <span id="footerYear">2019</span>
+ </div>
+ </footer>
+ <script>
+ function init() {
+ const footerYear = document.getElementById("footerYear");
+ footerYear.innerHTML = new Date().getUTCFullYear();
+ }
+ init();
+ </script>
+ </body>
</html>

Now imagine we opened the Merge Request, and it was approved.

We’re ready to merge. Now we can run git pull to get any new commits from other teammates and rebase our branch to update it with:

git flow feature rebase

Oh no! There is a conflict in our index file!

Will try to rebase 'dynamic-footer-year' which is based on 'develop'...
First, rewinding head to replay your work on top of it...
Applying: Footer shows current year dynamically
Using index info to reconstruct a base tree...
M index.html
Falling back to patching base and 3-way merge...
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
error: Failed to merge in the changes.
Patch failed at 0001 Footer shows current year dynamically
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

Git will try its best to figure out where everything is supposed to go, but in some instances, that is not possible, and user intervention is required.

Note: Git moved us into a separate branch to handle the conflicts without affecting the commits we already have.

If we open index.html in our editor, looks like this:

Conflicts on index.html

These lines mark conflicting blocks:

<<<<<<< HEAD=======>>>>>>> branch name

Oh, right, we forgot that we ran our HTML formatter, and it removed characters from every line.

Everything between <<<<<<< HEAD and ======= shows the block as it is in the base branch, in other words, the one that will receive our new feature (develop).

While everything between =======and >>>>>>> branch name shows the updates on our current branch feature/dynamic-footer-year

We might be tempted to remove everything from the base branch (develop) and keep only our new feature.

At first glance, blocks are pretty much the same, and we know that our formatter updated everything. Still, if we compare the two pieces of code, we’ll see that one of our teammates updated the footer to include a link to the first part of this guide and moved each footer section into its own container (they also forgot to add a quote, but let’s fix it for them).

If we removed the code from develop, we would remove their work and probably get an angry teammate.

Instead, we are going to modify the code so that both blocks can exist together. After all, our teammate was kind enough to move the year into its own container.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Learning Git Flow</title>
</head>
<body>
<header>
<h1>Learning Git Flow</h1>
</header>
<section>Coming soon!</section>
<footer>
<div>
<a
href="https://medium.com/gumgum-tech/git-and-git-flow-a-guide-871d46a0ebcb"
rel="noopener noreferrer"
>git and git-flow, a guide</a
>
</div>
<div>
<span id="footerYear">2019</span>
</div>
</footer>
<script>
function init() {
const footerYear = document.getElementById("footerYear");
footerYear.innerHTML = new Date().getUTCFullYear();
}
init();
</script>
</body>
</html>

Note: Don’t forget to remove the conflict markers!

We fixed the conflicts, saved the file, and closed our editor. How do we let Git know that the conflict is solved and that the rebase should continue?

Looking back at the message we got from running git flow feature rebase, we’ll get the answer:

Resolve all conflicts manually, mark them as resolved with:

git add/rm <conflicted files>

then run:

git rebase --continue 

You could also skip a commit with:

git rebase --skip

To abort and get back to the state before rebasing, run:

git rebase --abort

Just like when we create a commit, we will use git add index.html to let Git know that the conflict is over, and then instead of creating a new commit, we will run git rebase --continue to move on with the rebase we originally wanted to do.

Notes:

  • You can also do git rm filename if a file is not needed anymore (not our case).
  • You can use git rebase —-skip to ignore the conflicts and continue the rebase; this will probably break your program. I do not recommend it unless you plan on dealing with all of them together instead of individually, which may lead to way more problems.
  • You can leave the detached branch and go back to how everything was before attempting to rebase by running git rebase —-abort.

Now that our Merge Request is approved, the file is up to date and includes both our work and our teammate’s, we can merge it and close the branch with git flow feature finish or Gitlab’s UI.

Note: When merging a feature/bugfix/hotfix branch, the default behavior is to delete it. You can preserve it instead by running:

git flow feature finish -k

Don’t forget to push after merging!

Working with Epics

So far, we have worked on simple features that work individually and that after completion, we can merge to master or develop without affecting others’ work.

However, some functionality may be more complicated and require many different tickets to be considered complete. These tickets may be worked on in parallel by other team members or, sequentially, by ourselves.

Our next ticket is to create the main content of our index.html . It has two sections:

Product Info

  • It is on the left side
  • It shows the product title at the top
  • Product description at the bottom

Product Form

  • It is on the right side
  • Fields: name, email, comment text box, submit button
  • Upon submission, we will send a POST request to an endpoint.

The Product Info could work as a stand-alone section; however, our Imaginary Company is not only interested in showing product information, but also in getting customers’ opinions on the product, and the form itself would not make sense without the product information.

With these requirements, we can get to create sub-tickets and split the work among our peers. After some discussion, we come up with these tickets to split the work among the three team members assigned to this feature:

  • Create Product Info section
  • Create form UI
  • Create endpoint

Of course, three team members for this would be overkill in a real scenario, but for Imaginary Company, there’s nothing as too many resources on a small task.

By this point, we have four tickets dedicated to this task, one that covers the feature as a whole, our Epic, and three for the Epic’s sub-tasks.

Creating sub-task branches

Let’s start by creating the epic ticket just like any other feature:

git flow feature start create-main-content

As we saw before, this will move us into a new branch:

feature/create-main-content

The new branch will be the Epic base, and the sub-tasks will merge to it rather than develop.

“Isn’t this develop but with extra steps?” I hear you ask, and it’s a good question.

Imagine our fourth teammate is working on adding a description section to the file; our Project Manager deemed this task as high priority and to be released ASAP. Additionally, our teammate can complete this task in a shorter time than it will take us to finish all of the main content.

Say we complete the Product Info section before our teammate has the chance to finish the header.

After merging directly to develop, it wouldn’t be possible to release the header because develop now includes parts of an incomplete feature that Imaginary Company does not want to publish by itself.

We would have to revert our commit, hide the incomplete code, or put it behind feature flags. We can avoid these issues by creating the Epic branch and merging the sub-tasks into it.

Our team won’t work directly into the epic branch; instead, each team member will create a new branch for their assigned tickets. With this in mind, let’s publish it so others can use it as the base for theirs:

git flow feature publish

We will use the now-familiar git flow feature start command but passing it a new argument, our base branch’s name:

git flow feature start product-information feature/create-main-contentgit flow feature start product-form feature/create-main-contentgit flow feature start product-form-request feature/create-main-content

This time, after running any of these commands, we will get a slightly different message from git-flow, letting us know about the base branch:

Switched to a new branch ‘feature/product-information’Summary of actions:- A new branch ‘feature/product-information’ was created, based on ‘feature/create-main-content’- You are now on branch ‘feature/product-information’Now, start committing on your feature. When done, use:git flow feature finish product-information

Note: When passing the base branch name, we should give the name exactly as it appears in git, including the branch type (feature/, hotfix/, bugfix/, etc.)

The flow of our branches now looks like this:

master <- develop <- feature/create-main-content <- feature/product-information

Since all the tickets have been discussed and fleshed out, our fearless team sets out to develop all the tasks in parallel. After all, what one programmer can do in one month, two programmers can do in two months, right?

When we’re ready to open a Merge Request, we will have to select the base branch in our provider’s UI to make it aware of the same things git-flow knows, namely, that the base branch is feature/create-main-content rather than develop:

GitLab’s interface to pick base branches for Merge Requests

After our request is approved, we use the same command as before:

git flow feature finish

git-flow will show us again the summary of actions that lets us know where it merged our branch to and where we are now:

Summary of actions:- The feature branch ‘feature/product-information’ was merged into ‘feature/create-main-content’- Feature branch ‘feature/product-information’ has been locally deleted; it has been remotely deleted from ‘origin’- You are now on branch ‘feature/create-main-content’

Right after the team finished the Epic’s first task, our fourth teammate released the new version of our project, now including the description.

Avoiding conflicts with git rebase — onto

Our project’s latest release includes code that is not related directly to the features we’re developing, but it affects the same section we’re working on. We can update our Epic to get these changes and make sure our new additions are in the right place.

As we saw in part 1, we can run git flow feature rebase to update our branches. Since there is nothing new on our Epic branch yet, running the same command in the sub-task branches would do nothing:

Will try to rebase ‘product-form’ which is based on ‘feature/create-main-content’…Current branch feature/product-form is up to date.

We have to update our Epic branch first to get the updates, and then our sub-task branches:

Let’s go back to our feature branch with:

git switch feature/create-main-contentSwitched to branch ‘feature/create-main-content’Your branch is up to date with ‘origin/feature/create-main-content’.

We can rebase the Epic now:

git flow feature rebase

We get a conflict since our teammate added the new description in the same place we added the product info, but we already know how to solve these. ;)

<<<<<<< HEAD
<p>This is a sample project to showcase git to pics.</p>
=======
<div id="product-info">
<h2>Guide</h2>
<ol>
<li>
<a
href="https://medium.com/gumgum-tech/git-and-git-flow-a-guide-871d46a0ebcb"
rel="noopener noreferrer"
>Part 1</a
>
</li>
<li>
<a
href="https://medium.com/@edrpls/git-and-git-flow-part-2-8de67ba2dd31"
rel="noopener noreferrer"
>Part 2</a
>
</li>
</ol>
</div>
>>>>>>> Add product information section%

After solving the conflict, we continue with the rebase and push our changes with git push -f, since we modified the git history.

Now we’re ready to update our sub-task branches, let’s go back to our product form branch by running:

git switch feature/product-form

And now, to update:

git flow feature rebase

There’s a new conflict!

<<<<<<< HEAD
<p>This is a sample project to showcase git to pics.</p>
=======
>>>>>>> Add product information section

But how!? Didn’t we just fix that? Git should know better than this, right?

Well, yes, and it does, but there is a catch. It would be almost effortless to remove the lines git added and commit the file in this specific case, but what if we had ten conflicts or even more? It doesn’t make sense to fix stuff that we already fixed upstream.

We will abort this rebase with git rebase --abort and determine the cause for this conflict.

Let’s take a look at the base branch commits with:

git log feature/create-main-content

It looks like this:

commit 29be90f339f7742513415e2c1c42b952c47dcb96 (origin/feature/create-main-content, feature/create-main-content)
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:10:16 2021 -0600
Add product information sectioncommit a0001609fbc5d943dd56c24145b9b0de686d5b21 (origin/develop, develop)
Merge: 88753ae 50959e1
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:45:00 2021 -0600
Merge tag '1.0.1' into develop1.0.1commit 50959e1c105276d793e13d171a0ae3c17f13e409 (tag: 1.0.1, origin/master, origin/HEAD, master)
Merge: 232505f 88753ae
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:44:56 2021 -0600
Merge branch 'release/1.0.1'commit 88753aed4adcbfb4168a6a74546cfdb02e9fe0b2
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:42:36 2021 -0600
Add project description (#10)

And now, let’s look at our current branch commits (feature/product-form) with git log:

commit ed1ca5bfdee0568df481285609041e51d5b03884 (HEAD -> feature/product-form)
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:10:16 2021 -0600
Add product information section

Can you spot the main difference?

A photo of my dogs to break the huge text block

Answer in:

5

4

3

2

1

We have the commit “Add product information section” with the hash ed1ca5bfdee05 on our current branch, and on the base branch, we have the commit “Add product information section,” but its hash is 29be90f339f774.

Note: it is unnecessary to use the complete hash most of the time; they should be unique enough that using the first seven characters is usually enough.

Now we have two different commits that do the same thing, and if we were to fix the conflicts, we had earlier and continue with the rebase, both commits would show up on our history.

To avoid the duplicated commits, we will perform a rebase without the magic from git-flow to synchronize our histories again; this rebase will include the special flag --onto.

The rebase command we will use looks like this:

git rebase —-onto newBase oldBase

Where newBase is the commit on our base branch that we want to keep, and oldBase is the commit we want to ignore.

So, in our case, the entire command will be:

git rebase --onto 29be90f ed1ca5bFirst, rewinding head to replay your work on top of it…

That’s it! No conflicts, and if we look at our git log again, we see everything in its right place:

commit 29be90f339f7742513415e2c1c42b952c47dcb96 (origin/feature/create-main-content, feature/create-main-content)
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:10:16 2021 -0600
Add product information sectioncommit a0001609fbc5d943dd56c24145b9b0de686d5b21 (origin/develop, develop)
Merge: 88753ae 50959e1
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:45:00 2021 -0600
Merge tag '1.0.1' into develop1.0.1commit 50959e1c105276d793e13d171a0ae3c17f13e409 (tag: 1.0.1, origin/master, origin/HEAD, master)
Merge: 232505f 88753ae
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:44:56 2021 -0600
Merge branch 'release/1.0.1'commit 88753aed4adcbfb4168a6a74546cfdb02e9fe0b2
Author: Eder Sánchez <email@example.com>
Date: Mon Mar 29 19:42:36 2021 -0600
Add project description (#10)

Both our base and sub-task branches now include the updates from upstream (master/develop), and we avoided duplicate commits by using git rebase --onto. Our team can move forward with the Epic.

After all our sub-tasks are complete, we can open a new Merge Request for the Epic branch. In theory, this one shouldn’t require a review as intensive as the sub-tasks since we already had reviews for each of them. Nonetheless, it’s always a good idea to review the result of our sub-tasks.

Before merging, it’s also a good idea to review whether the Epic’s commits are relevant to the big picture or not. For this feature, I don’t think they are, as we can squash the whole feature into a single commit, so our three commits:

  • Add product information section
  • Add product form
  • Submit product form payload

Can become one:

“Add product information and form”

And that’s it, after approvals, reviews, and an interactive rebase (to squash commits), we can merge the Epic into develop with:

git flow feature finishSummary of actions:- The feature branch ‘feature/create-main-content’ was merged into ‘develop’- Feature branch ‘feature/create-main-content’ has been locally deleted; it has been remotely deleted from ‘origin’- You are now on branch ‘develop’

Note: Don’t forget to push to develop!

Finding the culprit with git blame

We finished our first “big” feature on time for the new sprint and got a new ticket. We need to add a line to separate the footer from the rest of the content.

As usual, we start by creating a new branch:

git flow feature start footer-divider

This is going to be easy. We open our index.html again, go to line 36, add a new line under it, and start writing our code <hr, and before we can close our tag, the project’s Slack channel goes crazy, we get a bunch of pings, our PM is on fire.

There was a terrible, terrible bug on our last release!

The description says “to pics” instead of “topics”! How could this happen!?

There’s no need to point fingers, but let’s imagine we do want to point fingers, we can run:

git blame index.html

This command will give us a line-by-line rundown of the index.html file that includes the commit hash that modified it, the author’s name, the date when they added it, and the line number.

88753aed (Eder Sánchez 2021–03–29 19:42:36 -0600 13) <p>This is a sample project to showcase git to pics.</p>

Eder does it again, at least partially, since this slipped through our review process. Not to worry, we can create a hotfix branch to fix this!

Oh, but wait, we’re in the middle of some changes that we have not committed yet. And if we try to move to a different branch, git will complain about it:

git switch mastererror: Your local changes to the following files would be overwritten by checkout:index.htmlPlease commit your changes or stash them before you switch branches.Aborting

We could commit the incomplete stuff with a prefix like WIP or something similar, but then we would have to rebase and squash this, but…

Instead, let’s stash it.

Stashing is a way to save and hide files temporarily without making changes to git’s history. It’s as simple as running:

git stashSaved working directory and index state WIP on footer-divider: 7b0cd34 Add product information and form

Running git status will not show updates to index.html.

Recovering those changes is as simple as running:

git stash popOn branch feature/footer-dividerChanges not staged for commit:
(use “git add <file>…” to update what will be committed)
(use “git restore <file>…” to discard changes in working directory)
modified: index.html

But for the moment, let’s keep them stashed away while we deal with the “bug” in production.

Fixing bugs in production

After stashing our work in progress, we’re able to create a hotfix branch.

Hotfix branches are unique because they skip the regular flow from develop to master, and instead, when finished, they are merged to both master and develop.

This process allows us to make a new release that includes only the patch and adds it to our develop branch so others can benefit from it.

To create a hotfix branch:

git flow hotfix start description-typo

After fixing the typo and running our usual workflow, we’re ready to merge the hotfix branch with:

git flow hotfix finish

Closing a hotfix will merge to master and develop and create a new tag of the current state of master.

Note: There are also bugfix branches that merge only to develop. These help with bugs that have not reached production or have low priority and can wait for the next release. They can be created with git flow bugfix start [branch name]

Git will ask us to enter a name for this tag. I will use “typo-fix” for this one. Tag names should describe the fixes we added:

typo-fix
#
# Write a message for tag:
# description-typo
# Lines starting with ‘#’ will be ignored.

After entering the name, saving and exiting the file, we should now be back at develop, and get a message like this:

Switched to branch ‘master’
Your branch is up to date with ‘origin/master’.
Merge made by the ‘recursive’ strategy.
index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Switched to branch ‘develop’
Your branch is up to date with ‘origin/develop’.
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Fatal: There were merge conflicts.

In our case, there’s a conflict since we edited the line with the typo to add our new container. Not a problem; we already know how to fix it. As opposed to conflicts while rebasing, we will have to create a new commit for the conflict resolution this time.

Now we can push to develop, go back to master and also push there:

git push # assuming we’re already on developgit switch master # move to mastergit push # push master

Depending on how your deployment is set up, you may want to push the branch and the tag we created earlier. In our case, however, the CI pipeline is configured to publish tags following the Semantic Versioning format:

major.minor.patch

Our previous tag doesn’t fit this format, so we will manually create a new tag that does match Semver by running:

git tag [tag name]

The last version published is 1.0.1. According to Semver, that would be our first major version, no minor releases yet, and one patch. If our next release is another patch, then the next version will be 1.0.2 and the command to create the tag:

git tag 1.0.2

This command doesn’t return anything, but we can confirm that our tag exists by running:

git tag1.0.0
1.0.1
1.0.2
description-typo
(END)

Note: this command will open a terminal pager that you can exit by pressing q

Now it’s just a matter of pushing the tag with:

git push origin 1.0.2Total 0 (delta 0), reused 0 (delta 0)
To gitlab.com:edr1/git-guide.git
* [new tag] 1.0.2 -> 1.0.2

Note: It’s a good idea first to push develop, then master, and finally, the tags. Our CI creates a preview build of develop, so we can avoid pushing to master yet and consuming extra resources if it fails. If develop is fine, master will probably be fine, and our release should work correctly.

Undoing git add

Having fixed the issue in production, we can go back to work on the ticket we had:

# go to the branch
git switch feature/footer-divider
# optionally, add fixes
git flow feature rebase
# recover stashed work
git stash pop

We finish the work we had pending, add it to our next commit with:

git add .

Oops, we had a few extra files in the directory that got included in the commit.

git statusOn branch feature/footer-divider
Changes to be committed:
(use “git restore — staged <file>…” to unstage)
new file: unrelated.md
new file: unrelated_2.md
modified: index.html
new file: unrelated_3.md

Since we have not created the commit, it’s easy to remove them:

We can do git reset to remove all of them or remove specific files with:

git reset [filename]

For demonstration purposes, let’s go with the second option:

git reset unrelated.md unrelated_2.md unrelated_3.md

Let’s check again what files will be included in the commit:

git statusOn branch feature/footer-divider
Changes to be committed:
(use “git restore — staged <file>…” to unstage)
modified: index.html
Untracked files:
(use “git add <file>…” to include in what will be committed)
unrelated.md
unrelated_2.md
unrelated_3.md

Great! Now we will add only the index.html file to the commit, and we’re ready to create our commit with:

git commit -m”Add divider above footer”# and to publish our branch
git flow feature publish

Now it’s just a matter of waiting for the review process to finish, and we’re done.

Other useful git commands

The last ticket marked version 1.1.0(one major, one minor, no patches for the minor version) and our project is public.

Let’s take a look at our hard work:

It ain’t much but it’s honest work

Now that we can relax let’s look at some commands that can help us work with Git.

See a short version of the log, grouped by author:

git shortlogEder Sanchez (6):
Create the README (#1)
Create the index.html page (#3)
Merge branch ‘release/1.0.0’
Merge tag ‘1.0.0’ into develop
Merge branch ‘feature/footer-guide-link’ into ‘develop’
Merge branch ‘feature/dynamic-footer-year’ into ‘develop’
Eder Sánchez (11):
Add link to guide in footer (#5)
Footer shows current year dynamically
Add project description (#10)
Merge branch ‘release/1.0.1’
Merge tag ‘1.0.1’ into develop
Add product information and form
Fix “to pics” typo
Merge branch ‘hotfix/description-typo’
conflict fix
Add divider above footer
Merge branch ‘release/1.1.0’
(END)

See a file from a different branch:

git show [reference]:[path to file]

Where reference can be a commit’s hash or a branch, for example, these two are equivalent in the guide’s project history:

git show develop:index.htmlgit show fe452d2bf4d416:index.html

You can then pipe the files to other processes:

git show develop:index.html | grep -i email<label for=”email”>Email</label>
<input type=”email” id=”email” name=”email” />
email: elements.email.value,

Or write them to your branch:

git show develop:index.html > develop_index.htmlgit statusOn branch master
Your branch is up to date with ‘origin/master’.
Untracked files:
(use “git add <file>…” to include in what will be committed)
develop_index.html

See all active branches:

git branch

Delete a branch

git branch -d

For this command to work, the branch has to be already merged upstream. Otherwise, see the command below.

Force delete a branch:

git branch -D [branch-name]

Add specific commits from other branches:

git cherry-pick [commit hash]

You can also pick a range of commits:

git cherry-pick [first commit hash]..[last commit hash]

Compare branches:

Compare our current branch with upstream:

git diff

Compare any two branches:

git diff branch1 branch2

Grep (search) a word in tracked files:

git grep [word or expression]

Revert a commit

Occasionally, we might introduce broken functionality, and it may be needed to undo it. We can revert a commit by running:

git revert [commit hash]

The command above will create a new commit undoing whatever happened in our commit.

This command also allows us to revert a range of commits:

git revert -n develop~4..develop~1

With this command, we will revert from the fourth last commit in develop to the second commit (the range end is exclusive). Additionally, by using the flag -n, we specify that we don’t want to generate new commits, just remove the commits.

Learn more about any Git command:

Git’s documentation is excellent! You can find the complete documentation for any command in this guide with:

man git [command]

Example:

man git blame

Conclusion

In this part of the guide, we covered some functionality to complement the first part. These are common scenarios that I’ve found working with Git and git-flow over my career and the current workflow we use at GumGum.

We learned:

  • To fix conflicts when the same file and line are modified in two different branches.
  • To work with Epics and Sub-Tasks.
  • To use hotfix branches to fix production bugs
  • How to use various commands for various purposes.

I aim for this guide to be enough for you to work comfortably with git and git-flow; however, this barely scratches the surface, all of the commands we used in this guide accept many options and flags. They can provide lots of functionality, but covering every one of them is a job better suited for git’s documentation; hence, I encourage you to use the man command to learn more about them or dive into the online documentation.

After two years of hiatus, this humble guide is finally complete.

Read the first part here.

We’re always looking for new talent! View jobs.

Follow us: Facebook | Twitter | | Linkedin | Instagram

--

--