Please post proper Git workflow

Whenever I try to do a pull request, I tend to get a unholy mess of all commits sent every time…

My attempted workflow is as follows:
git clone (from my fork at github)
git checkout -b my_new_feature
…work and commit some stuff
git push origin my_new_feature (origin is my fork)
…at, I do a pull request - so far so good.
git checkout -b my_new_feature2
…work and commit some stuff
git push origin my_new_feature (origin is my fork)
…at, I do a pull request - , and now I see both my_new_feature, and my_new_feature2 being submitted.

I tried to squeeze "git rebase master"in here and there.
tried git pull --rebase upstream master

what’s the proper way of doing things here ?

also, when I do “git rebase upstream master” - I instantly get my last commits on top of that:

git rebase upstream/master
First, rewinding head to replay your work on top of it…
Applying: Mission: added spline waypoint
Applying: Added some Pixhawk mavlink definitions.

these commits are also listed in any new branch I make.


I haven’t worked with Github much… but have worked in software for a lot of years. I’ve tried to find basic help, but nobody seems to be willing to put their necks on the line.

Maybe because github (Git) isn’t at all intuitive. I suppose once you’ve worked with it for a while it begins to make sense… but in the meantime…

I’m not sure where git was “born” I think I saw it first on linux? I don’t know. Linux isn’t at all point and click like Windows (for instance), BUT it IS terribly intuitive. Scripts do everything, and usually writing a script is a question of taking one step after another and making sure that you have taken into account the considerations for when a certain script fails.

But, GIT seems to do everything backwards… and I suppose, the perspective is from the project’s point of view, not the programmers. So when you ask for a pull, you want someone to to accept what you’ve done, pull it into the project. Well I’m sure you know that…

I just recently did my first modification. So I am going through the same steps as you, but after some time, reading, and testing and loading this and that software. Although I work well with linux, the Windows GitHub has helped me a lot. Since everything seems backwards, I let windows work out the syntax. About the only thing I had to do by hand (on the console) was the rebase… and I HOPE I did it right. Although I didn’t get any errors.

At one point during the first attempt to do the rebase, I got a lot of messages similar to yours., but in the end, I think it resulted in a project ready for a pull request.

I couldn’t really tell you much about the hand typed commands, as I said, I let Windows do the dirty work.

But basically you have to commit first. In my case I had a bunch of junk in the folder that the compiler created that Windows seemed to want to include in the commit. I just deleted it (not relevant to the compile as such, just files that the GUI used (Atmel).

Once you get the commit accepted (no conflicts with master etc) you are ready to rebase… I think this just makes sure that everybody gets the credit for their own changes. I don’t know, but somehow in the process, everything can all get mixed into one pot and the rebase sorts out who did what. If a file somehow gets touched in the process, which you didn’t modify, it could be attributed to you, when you really didn’t have anything to do with it. (Something like that).

Once rebased You can make a pull request. That’s when they tell you that you have to change a bunch of stuff first! :slight_smile: and go through the whole process again.

I think in your case, since the first pull request hadn’t been accepted yet (I assume) the second request bunched the two together.

I don’t know, I probably haven’t helped much. But I will be watching here to see if there are any better responses… I need them too.

Git requires a steep learning curve and it helps tremendously, if you understand what it is doing under the covers. I think it took me a few months of trying it out here and there, until I finally had a revelation and everything started to make sense. This site did the trick for me…

That being said, what you want as a Git workflow is to present a few (lets say 1 to 5) commits as a Pull Request to the upstream repository. Those commits should ideally be based on top of the master commit on that upstream repository. So how do you get that?

Day 1
I fork the upstream repo on the GitHub website, then clone it locally to my disk. (At this point, there are 3 repositories in existence: The original “UPSTREAM” on GitHub, the forked “ORIGIN” on GitHub and the local “LOCAL” repository on my disk. All of these 3 repos are identical at this point - but they are 3 different instances, connected via various magical strings (UPSTREAM <-> ORIGIN is maintained by GitHub magic, ORIGIN<->LOCAL is maintained by a remote on my LOCAL repository). Also note that when I cloned your LOCAL, Git went ahead and checked out the “master” branch.

MentalPause: A branch in Git is a pointer to a commit. A branch just points to a commit. In C: typedef struct Commit_t *Branch_t; Branch_t *b = &myCommit; (make sure this sinks in, read the site mentioned in the first paragraph in order to understand this). /MentalPause

So there’s a branch in the “upstream” repository called “master”, which points to a commit A. We shall refer to this as upstream/master. There’s another branch in my origin repository that is also called “master” and that one points to the same commit A; lets call this one “origin/master”. And there’s one more branch, also called “master”, which resides on my disk and… that’s just plain old master. My local repository knows about the latter two right now (git branch -va). In order to “get” to the upstream one, I need to define a local remote that points to the upstream repository:

git remote add upstream git fetch upstream

Day 2
I’ve decided to enhance the code base and implement feature “parachute”. I enter my local repository and start a new branch:

MentalPause: The new branch starts out his life pointing at the same commit that was checked out before I typed my command. Since I haven’t done anything since yesterday, this happens to be commit A, which my master points to. I know that’s also where origin/master points to, since I’m the only guy working on my repository on GitHub, but I actually don’t know if upstream/master also still points to Commit A. Maybe someone else worked on upstream and added a commit or two and then upstream/master is already ahead of me. Oh well, no worries, I’ll fix that later. /MentalPause.

I start modifying files to implement the feature. Turns out there are two sets of changes required to make this work. I decide to split these changes into two commits as well.

git add parachute.c parachute.h git commit -m "P1: parachute implementation" git add mavlink.c git commit -m "P2: mavlink parachute command"

Where are our branches pointing at?
upstream/master -> ??
origin/master -> A
master -> A
feature_parachute -> P2

The local log looks like this: A -> P1 -> P2.

If we were to send this upstream (as a pull request), it wouldn’t necessarily work. Maybe there are changes up there that conflict with ours. So before sending, we need to make sure that my commits “apply”/“merge” properly. We do this, by “rebasing” our changes to the current upstream/master. First off, lets find out the current state of upstream:

Oh my, people have been busy. upstream/master points to commit D (the log shows A->B->C->D). Three commits have been added since yesterday. And one of them (commit C) modifies the file mavlink.c. This is bound to create conflicts. I better fix them now before submitting the pull request, which will only cause grief in its current form.

Tip: Git always works on the branch that is currently checked out.

So I have feature_parachute checked out. I now run this:

What this command does is pretty amazing. First off, we’re right now on commit P2. upstream/master points to D. Git will walk the history backwards and find the commit which P2 and D have in common (that’s A). It will then checkout A (and therefore throw out all the changes I have in P1 and P2). Then it starts applying B, C and D. Now the working directory looks like D. And here comes the amazing part: Git figures out the lines that changed in every file to transform A to P1. Mathematically speaking, that’s (P1 - A). It then adds that difference to the current folder (D). This will create P1’ (which is D + (P1 - A)). So the new commit P1’ is almost identical to the other P1, except it has a different parent. The parent of P1 is A, the parent of P1’ is D. So, strictly speaking, P1’ is not the same commit as P1. Even though the commit message is the same, the author is the same, the line changes in the files are the same … but the parent is different. So this is a brand-new commit. I will even call this a new letter: E. It looks like P1, but it isn’t. Make sure you understand this! Git then continues and takes the difference between P1 and P2 (P2-P1) and applies that to E in order to create … F. Again, superficially, F looks deceptively similar to P2. But it is a new, different, independent commit. Finally, Git takes your pointer (the branch called “feature_parachute”), which was pointing to P2 and makes it point to F. Phew.

TLDR: sorry, no shortcuts - understanding how Git works is the key to using it./TLDR

So git rebase re-creates all of “our” commits on top of the indicated branch and moves our current branch to the last of this brand-new commits. It doesn’t modify upstream/master itself. That’s just the destination of our commits.

Of course, the story above didn’t finish with a happy ending… there was a hiccup. Just as Git was about to apply the difference (P2-P1), it realizes that it conflicted with some changes introduced with commit C. So it stopped, spat out an error message and now I have to go in and fix it. This typically involves editing the file and removing the merge conflict markers and fixing the code. Once this was done, I continued the rebase operation with

Problem? If things get too hairy and the rebase just looks too disastrous. Don’t worry. There’s an emergency exit. While the conflicts pile up and there’s no way to fix them right now, I can type

and everything is back to where I started. I’m on branch feature_parachute, and it points to P2. The real P2. The grandson of A. /Problem?

I push this to my origin with

and use the browser UI to create the pull request. Send it off. Done.

Day 3
I have another great idea. Feature “backwards flight” is going to be awesome. Lets code it up.

But first. Wait. Yesterday I left off being on commit F (which looks just like P2) in branch ‘feature_parachute’. That is still in review and being torn apart. This new work should be independent of that. Lets start fresh. I want to start with the latest, bleeding edge commit available: whatever master is pointing at in upstream!

Now my local disk repo knows where upstream/master points to (turns out, the world was lazy and it still points at D - my pull request hasn’t been merged yet either).

By giving the checkout command a starting point for the new branch, it doesn’t start on commit F, which is currently checked out in my local clone. Instead, it goes back to D and then creates the new branch here.

Rinse repeat.

Day 4
I decided that it might be cooler to find out how Git works, than to write code. I spend the entire day figuring out exactly how I can rewrite my local history using ‘git rebase --interactive’.

Day 27
I should fly more.

I just wrote this big article with minimal checking on accuracy of spelling and command correctness. My main point is that you need to find out how Git works and then all these operations will start to make sense. I hope you had fun reading it though :sunglasses:

1 Like

Thank you - this is by far the best description of real-life use I’ve seen.
Will read it carefully and practice it as soon as possible.
I’ve googled a lot, different solutions for different “problems” , but this provides the “right” way to do it, and should make me able to finally understand what I did wrong.

Thank you. Looking forward to finally spend more time on commits than (self caused) git troubleshooting. :slight_smile:

FYI: my problem was that I did not use

I always used

and so I got all the past commits. :slight_smile:

Thank you.

one more question:
Now that I’ve made many new features on several branches, made pull requests, but the changes are not pulled yet.
I would like to have my own version with all the new goodies. - should I make another branch and cherrypick everything from the other branches, or is there another, better way ?