[git] Pushing commit(s) after pulling
I share a library with others and make some commit to a branch of that library. Then I try to 'push' the commit, only to be told that there are changes that have been made which I need to 'pull'. So I 'pull' the latest changes for that branch, and luckily there is no conflict(s) with my local commit(s). What do I do now ? I can 'push' my changes successfully but then the history of my particular changes appears to be lost. Am I supposed to revert my changes to the local repository by doing a 'reset' and then reapply those changes ? That seems onerous. What is the right way to 'push' my changes and still preserve the history of each one of them on the remote repository ?
On 19/08/2014 14:53, Edward Diener wrote:
I share a library with others and make some commit to a branch of that library. Then I try to 'push' the commit, only to be told that there are changes that have been made which I need to 'pull'. So I 'pull' the latest changes for that branch, and luckily there is no conflict(s) with my local commit(s).
What do I do now ?
I can 'push' my changes successfully but then the history of my particular changes appears to be lost.
Lost in what way?
Am I supposed to revert my changes to the local repository by doing a 'reset' and then reapply those changes ? That seems onerous.
What is the right way to 'push' my changes and still preserve the history of each one of them on the remote repository ?
What should have happened is something like this: A - B - D - F \ / C - E Where A/B are the common ancestors of the changes (B is the version that you last fetched). You made commits C & E in your local copy, but in the meantime your collaborators made commit D and pushed it. When you try to push E, git will warn you that you don't have the latest code and you should pull. When you "git pull", you will fetch D and then git will merge this to create F. You should now be able to push F and it will include all changes from both sides and the revision graph will look like the above. (git's revision graph is a very powerful and handy thing, completely unlike svn's graph, which is fairly useless; if in doubt, always look at the graph.) There's an alternate method of merging changes ("git pull --rebase") that would instead result in this graph: A - B - D - C' - E' ie. git fetches D, then rebases your original changes C and E onto it, thereby pretending that you created them after D as far as the history is concerned. Some people prefer this, others dislike it, but it's important to note that rebasing is a history-modifying action and you should never do it on anything that has already been pushed publicly (note that the original C and E are "destroyed" during the course of the above -- this means that if anyone else had based further work on one of these they would be forced to rebase in turn to get their copy back into a sane state). Which sounds a little scary when you first read it, but once you get your head around it can be quite handy when used correctly.
On 8/18/2014 11:35 PM, Gavin Lambert wrote:
On 19/08/2014 14:53, Edward Diener wrote:
I share a library with others and make some commit to a branch of that library. Then I try to 'push' the commit, only to be told that there are changes that have been made which I need to 'pull'. So I 'pull' the latest changes for that branch, and luckily there is no conflict(s) with my local commit(s).
What do I do now ?
I can 'push' my changes successfully but then the history of my particular changes appears to be lost.
Lost in what way?
The message in the log for my push says: 'Merge branch 'develop' of https://github.com/boostorg/build into develop' rather than the actual commit message for my local repository change.
Am I supposed to revert my changes to the local repository by doing a 'reset' and then reapply those changes ? That seems onerous.
What is the right way to 'push' my changes and still preserve the history of each one of them on the remote repository ?
What should have happened is something like this:
A - B - D - F \ / C - E
Where A/B are the common ancestors of the changes (B is the version that you last fetched). You made commits C & E in your local copy, but in the meantime your collaborators made commit D and pushed it. When you try to push E, git will warn you that you don't have the latest code and you should pull.
When you "git pull", you will fetch D and then git will merge this to create F. You should now be able to push F and it will include all changes from both sides and the revision graph will look like the above. (git's revision graph is a very powerful and handy thing, completely unlike svn's graph, which is fairly useless; if in doubt, always look at the graph.)
There's an alternate method of merging changes ("git pull --rebase") that would instead result in this graph:
A - B - D - C' - E'
ie. git fetches D, then rebases your original changes C and E onto it, thereby pretending that you created them after D as far as the history is concerned. Some people prefer this, others dislike it, but it's important to note that rebasing is a history-modifying action and you should never do it on anything that has already been pushed publicly (note that the original C and E are "destroyed" during the course of the above -- this means that if anyone else had based further work on one of these they would be forced to rebase in turn to get their copy back into a sane state). Which sounds a little scary when you first read it, but once you get your head around it can be quite handy when used correctly.
I think I was supposed to do the "git pull --rebase" so that my change is applied on top of the pull's latest. When I use Tortoise Git there is no "Pull&Rebase" but there is a "Fetch&Rebase" so maybe that is what I am supposed to use in tgit. Thanks !
On 19/08/2014 16:18, Edward Diener wrote:
Lost in what way?
The message in the log for my push says:
'Merge branch 'develop' of https://github.com/boostorg/build into develop'
rather than the actual commit message for my local repository change.
That's actually correct though (albeit confusingly worded). Have another look at this:
A - B - D - F \ / C - E
Your current head will be F, so that's the commit message you're seeing, which is just a merge of D & E. The commit messages for C, D, and E themselves are still intact and you should see them if you look at the log. (When you push F, C & E come along for the ride automatically.)
I think I was supposed to do the "git pull --rebase" so that my change is applied on top of the pull's latest.
Possibly. As I said this tends to be one of those bikeshed arguments, with some people arguing that it should always be used and others arguing that it should never be used. (It mostly revolves around the commit messages being more readable with rebase, but the actual development timeline being more visible without.) Coming from an SVN background I think a rebased pull is the easiest option to get your head around, since it's similar to how SVN itself works (it does an invisible merge between your uncommitted changes and the incoming changes whenever you do an Update); the difference is just while in SVN you have a single clump of uncommitted changes when this invisible merge happens, in Git you can have many separate committed clumps of changes. The other related option is "squash"; it's similar to a rebase except that all your changes are folded into a single commit, ie. instead of this graph: A - B - D - C' - E' you get this one: A - B - D - F where F contains both C & E in a single commit. This is probably the variant that's the most like an SVN Update&Commit cycle, and again some people prefer this (particularly if their local commits aren't logically divided, maybe just multiple parking commits that don't all compile or pass tests) while others (perhaps more organised with their commits) prefer to retain each individual commit as a logical unit.
Gavin, On 08/19/2014 11:58 AM, Gavin Lambert wrote:
On 19/08/2014 16:18, Edward Diener wrote:
Lost in what way?
The message in the log for my push says:
'Merge branch 'develop' of https://github.com/boostorg/build into develop'
rather than the actual commit message for my local repository change.
That's actually correct though (albeit confusingly worded). Have another look at this:
A - B - D - F \ / C - E
Your current head will be F, so that's the commit message you're seeing, which is just a merge of D & E. The commit messages for C, D, and E themselves are still intact and you should see them if you look at the log. (When you push F, C & E come along for the ride automatically.)
I think I was supposed to do the "git pull --rebase" so that my change is applied on top of the pull's latest.
Possibly. As I said this tends to be one of those bikeshed arguments, with some people arguing that it should always be used and others arguing that it should never be used. (It mostly revolves around the commit messages being more readable with rebase, but the actual development timeline being more visible without.)
Coming from an SVN background I think a rebased pull is the easiest option to get your head around, since it's similar to how SVN itself works (it does an invisible merge between your uncommitted changes and the incoming changes whenever you do an Update); the difference is just while in SVN you have a single clump of uncommitted changes when this invisible merge happens, in Git you can have many separate committed clumps of changes.
The other related option is "squash"; it's similar to a rebase except that all your changes are folded into a single commit, ie. instead of this graph:
A - B - D - C' - E'
you get this one:
A - B - D - F
where F contains both C & E in a single commit. This is probably the variant that's the most like an SVN Update&Commit cycle, and again some people prefer this (particularly if their local commits aren't logically divided, maybe just multiple parking commits that don't all compile or pass tests) while others (perhaps more organised with their commits) prefer to retain each individual commit as a logical unit.
Thanks for writing this down in detail! It's indeed a matter of preference, although: - For Boost.Build specifically, I prefer to avoid merge commits as much as possible. - In most cases of a single commit, or a few unrelated commits, using "git pull" and creating merge commits results in rather noisy and unclear history. I'd imagine maintainers of other components might prefer to avoid that - although it's up to them, of course. Thanks, Volodya
On 20/08/2014 02:53, Vladimir Prus wrote:
Thanks for writing this down in detail! It's indeed a matter of preference, although:
- For Boost.Build specifically, I prefer to avoid merge commits as much as possible. - In most cases of a single commit, or a few unrelated commits, using "git pull" and creating merge commits results in rather noisy and unclear history. I'd imagine maintainers of other components might prefer to avoid that - although it's up to them, of course.
One case where it's a bit trickier to define the "right thing" is with pull requests. The simplest thing to do (especially if you're using the GitHub UI) is to simply merge the pull request directly. Obviously this does create a merge commit and you'll see a fork in the revision graph as in my first diagram. Often the pull request is created in isolation (especially if the user was using the GitHub editing UI directly) and so the maintainer could reasonably safely do a rebased or squashed pull on this branch to keep the core history "clean". This can be especially tempting if there were multiple commits to the PR branch during its lifetime to correct various issues raised by the maintainer -- then a squashed merge would produce a single "pristine" commit without all of the fumbling. The downside of the latter is that the history will no longer show which PR branch it came from (that's the part you're cleaning) -- and if the user didn't create it as a throwaway but is using it in one of their own branches (which is likely if they're using git themselves for their own software) then the former will just work nicely for them while the latter will create a conflict that forces them to rebase anything built on top of it.
On 08/20/2014 05:18 AM, Gavin Lambert wrote:
On 20/08/2014 02:53, Vladimir Prus wrote:
Thanks for writing this down in detail! It's indeed a matter of preference, although:
- For Boost.Build specifically, I prefer to avoid merge commits as much as possible. - In most cases of a single commit, or a few unrelated commits, using "git pull" and creating merge commits results in rather noisy and unclear history. I'd imagine maintainers of other components might prefer to avoid that - although it's up to them, of course.
One case where it's a bit trickier to define the "right thing" is with pull requests.
The simplest thing to do (especially if you're using the GitHub UI) is to simply merge the pull request directly. Obviously this does create a merge commit and you'll see a fork in the revision graph as in my first diagram.
Often the pull request is created in isolation (especially if the user was using the GitHub editing UI directly) and so the maintainer could reasonably safely do a rebased or squashed pull on this branch to keep the core history "clean". This can be especially tempting if there were multiple commits to the PR branch during its lifetime to correct various issues raised by the maintainer -- then a squashed merge would produce a single "pristine" commit without all of the fumbling.
Yep, I normally merge pull requests by hand, using "git fetch" + "git cherry-pick" of FETCH_HEAD - optionally squashing.
The downside of the latter is that the history will no longer show which PR branch it came from (that's the part you're cleaning) -- and if the user didn't create it as a throwaway but is using it in one of their own branches (which is likely if they're using git themselves for their own software) then the former will just work nicely for them while the latter will create a conflict that forces them to rebase anything built on top of it.
You are correct. Was not a problem in practice for me though - the branches being pull-requested had temporary-sounding names. - Volodya
On 20/08/14 03:18, Gavin Lambert wrote:
One case where it's a bit trickier to define the "right thing" is with pull requests.
For me it makes sense to merge pull requests rather than rebase them. They're usually a set of commits that fix a bug or implement a new feature, and are strongly correlated. It's different from the random merges you get in the "pull before push" scenario.
On 19/08/14 16:53, Vladimir Prus wrote:
Thanks for writing this down in detail! It's indeed a matter of preference, although:
I don't think it should be a matter of preference. You should always avoid publishing unnecessary noise. Don't merge when you can rebase. Of course, you should never rebase changes that you have already shared with others. It's also a good idea sometimes to squash commits, split them or just completely re-do them before pushing. How you want to present your work to the other members of your team is up to you.
If you can run 'gitk --all', it's very helpful for seeing your commits and branches and gives a reasonable interface for some features like cherry picking or branching on a commit. Gitk runs on Linux/cygwin, not sure about others. The next time you've made changes, run before you synch, after git fetch, and after git pull paying attention to where the markers are. Then you can decide if you want to rebase or merge by how history looks, it's also pretty easy to try alternates out on scratch branches. This, and git gui, are what got me over the git hurdle and start really enjoying using git. Hope it helps. PS apologize for the top post. ________________________________ From: Boost on behalf of Edward Diener Sent: Monday, August 18, 2014 7:53:03 PM To: boost@lists.boost.org Subject: [boost] [git] Pushing commit(s) after pulling I share a library with others and make some commit to a branch of that library. Then I try to 'push' the commit, only to be told that there are changes that have been made which I need to 'pull'. So I 'pull' the latest changes for that branch, and luckily there is no conflict(s) with my local commit(s). What do I do now ? I can 'push' my changes successfully but then the history of my particular changes appears to be lost. Am I supposed to revert my changes to the local repository by doing a 'reset' and then reapply those changes ? That seems onerous. What is the right way to 'push' my changes and still preserve the history of each one of them on the remote repository ? _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (5)
-
Edward Diener
-
Gavin Lambert
-
Mathias Gaunard
-
Schrom, Brian T
-
Vladimir Prus