{"thread":{"id":"35785","subject":"Determining update/merge/current state of a workspace","startedAt":"2014-02-02T22:15:09Z","lastAt":"2014-02-03T00:50:29Z","messageCount":4,"participants":["Stephen Leake","Jeff King","David Aguilar","brian m. carlson"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"234061","messageId":"85ppn540wi.fsf@stephe-leake.org","threadId":"35785","inReplyTo":null,"subject":"Determining update/merge/current state of a workspace","fromName":"Stephen Leake","fromEmail":"stephen_leake@stephe-leake.org","sentAt":"2014-02-02T22:15:09Z","receivedAt":"2014-02-02T22:15:09Z","isPatch":false,"sender":{"key":"stephen_leake@stephe-leake.org","avatar":null},"body":"I'm working on the DVC Emacs front-end for git\n(http://www.emacswiki.org/emacs/DistributedVersionControl), adding\nfeatures similar to the ones I added for monotone\n(http://www.monotone.ca). I'm used to monotone and new to git, so this\nmay seem like an odd workflow.\n\nI always do 'fetch' and 'merge' separately, never 'pull'. So after a\n'fetch', the DVC Emacs front end must determine what needs to happen\nnext. I think there are three cases:\n\n1) 'fetch' did not retrieve any revisions from remote; the last local\n   commit is the head of the branch.\n\n    The workspace is up to date (it may need to be comitted).\n\n2) 'fetch' retrieved revisions, and there were no local commits since\n   the previous fetch.\n\n    The last fetch is the head of the branch; if not equal to HEAD, the\n    workspace needs to be updated (via 'merge').\n\n3) fetch retrieved revisions, and there were local commits since\n   the previous fetch.\n\n   There are two heads for the branch (the two described above), they\n   need to be merged, then the workspace updated.\n\nI'm not sure how 'git fetch' handles case 3); I have not tested that\ncase yet.\n\nThe question I have is:\n\nWhat git queries can I run to determine which of the three states the\ncurrent workspace is in?\n\n'rev-parse HEAD' gives the last workspace commit.\n\n'rev-parse refs/remotes/<remote>/<branch>' gives the head of the branch\nin the remote repository as of the most recent fetch.\n\nBut to distinguish among the cases, I need to determine if one of these\ntwo revs is a child of the other or not. I don't see a git query to\ndetermine that directly.\n\nI could try parsing a 'log' output; I have not investigated that.\n\nThis is easy in monotone; there is a command 'mtn heads' that gives this\nresult directly (it returns either one or two revs), and another command\n'mtn automate toposort' that orders revs topologically (by parent/child\nrelationships).\n\n-- \n-- Stephe\n"},{"id":"234062","messageId":"20140202224453.GA16196@sigill.intra.peff.net","threadId":"35785","inReplyTo":"85ppn540wi.fsf@stephe-leake.org","subject":"Re: Determining update/merge/current state of a workspace","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-02-02T22:44:54Z","receivedAt":"2014-02-02T22:44:54Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Feb 02, 2014 at 04:15:09PM -0600, Stephen Leake wrote:\n\n> I always do 'fetch' and 'merge' separately, never 'pull'. So after a\n> 'fetch', the DVC Emacs front end must determine what needs to happen\n> next. I think there are three cases:\n\nDoing the two steps separately is common in git, too. The cases you\nmention are also something people commonly care about. Both \"git\ncheckout\" and \"git status\" will print out the relationship between the\ncurrent branch and its \"upstream\". These are sometimes referred to as\nahead/behind messages in the manual (because they are of the form \"You\nare N commits ahead of origin/master\", etc).\n\n> 3) fetch retrieved revisions, and there were local commits since\n>    the previous fetch.\n> \n>    There are two heads for the branch (the two described above), they\n>    need to be merged, then the workspace updated.\n> \n> I'm not sure how 'git fetch' handles case 3); I have not tested that\n> case yet.\n\nGit's fetch does not have to care about this case. It is responsible\nonly for updating the ref that keeps track of the remote side (e.g.,\nrefs/remotes/origin/master). Unlike in some other DVCSs, there is no\nglobal concept of a \"branch\" in git. The ref \"refs/heads/master\" refers\nto your local branch named \"master\", and the ref \"refs/remotes/origin/master\"\nrefers to some remote's branch with the same name. You can reconcile\nthem whenever and however you like, and do not have to do so immediately\n(or at all).\n\n> The question I have is:\n> \n> What git queries can I run to determine which of the three states the\n> current workspace is in?\n\nIf you want to know the relationship between two (or more) commits, you\ncan use `git rev-list` to enumerate them. You can use the symmetric\ndifference operator (\"...\") to walk both sides down to their merge-base.\nThe `--left-right` option will label them according to which side each\ncommit comes from. So try:\n\n  git rev-list --left-right @{upstream}...HEAD\n\nto see the commits, or just:\n\n  git rev-list --left-right --count @{upstream}...HEAD\n\nto just get the counts on each side. Note that I used \"@{upstream}\"\nthere instead of naming the branch specifically. The default remote\nbranch with which a local branch will merge can be configured, and does\nnot have to have the same name (or even be a remote branch).\n\n> But to distinguish among the cases, I need to determine if one of these\n> two revs is a child of the other or not. I don't see a git query to\n> determine that directly.\n\nI think what I gave above matches what you are looking for most\ndirectly. But as you may have already guessed, you can also use rev-list\nto find whether one rev is a child of the other (e.g., \"git rev-list\n--count a..b\" != 0).\n\n> I could try parsing a 'log' output; I have not investigated that.\n\nDon't do that. As you might expect, `git log` is built on top of the\ntraversals done by `rev-list`. The latter is preferred as a building\nblock, because it is \"plumbing\" whose output is guaranteed not to change\nin later git versions.\n\nI hope that helps,\n\n-Peff\n"},{"id":"234064","messageId":"20140202230456.GA56790@gmail.com","threadId":"35785","inReplyTo":"85ppn540wi.fsf@stephe-leake.org","subject":"Re: Determining update/merge/current state of a workspace","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2014-02-02T23:04:59Z","receivedAt":"2014-02-02T23:04:59Z","isPatch":false,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Sun, Feb 02, 2014 at 04:15:09PM -0600, Stephen Leake wrote:\n> I'm working on the DVC Emacs front-end for git\n> (http://www.emacswiki.org/emacs/DistributedVersionControl), adding\n> features similar to the ones I added for monotone\n> (http://www.monotone.ca). I'm used to monotone and new to git, so this\n> may seem like an odd workflow.\n> \n> I always do 'fetch' and 'merge' separately, never 'pull'. So after a\n> 'fetch', the DVC Emacs front end must determine what needs to happen\n> next. I think there are three cases:\n> \n> 1) 'fetch' did not retrieve any revisions from remote; the last local\n>    commit is the head of the branch.\n> \n>     The workspace is up to date (it may need to be comitted).\n> \n> 2) 'fetch' retrieved revisions, and there were no local commits since\n>    the previous fetch.\n> \n>     The last fetch is the head of the branch; if not equal to HEAD, the\n>     workspace needs to be updated (via 'merge').\n> \n> 3) fetch retrieved revisions, and there were local commits since\n>    the previous fetch.\n> \n>    There are two heads for the branch (the two described above), they\n>    need to be merged, then the workspace updated.\n> \n> I'm not sure how 'git fetch' handles case 3); I have not tested that\n> case yet.\n\nFetch updates your cached origin/master ref to match the remote.\nYour local master branch and worktree are left as-is.\n\n> The question I have is:\n> \n> What git queries can I run to determine which of the three states the\n> current workspace is in?\n> \n> 'rev-parse HEAD' gives the last workspace commit.\n> \n> 'rev-parse refs/remotes/<remote>/<branch>' gives the head of the branch\n> in the remote repository as of the most recent fetch.\n> \n> But to distinguish among the cases, I need to determine if one of these\n> two revs is a child of the other or not. I don't see a git query to\n> determine that directly.\n\nI think you're looking for \"git merge-base\".\n\nIf you do `git merge-base HEAD origin/master`\nand its result is equal to `git rev-parse HEAD`\nthen you know that master is an ancestor of origin/master\nand can be trivially fast-forwarded to origin/master.\n\nIf you get a SHA-1 that is not equal then there are probably[*]\nlocal commits that have happened on master and it should probably\nbe rebased (or merged).\n\n[*] other possibilities: someone rebased your upstream, etc.\n\n\nPeople have differing opinions on how to resolve the diverging\nhistory. Topic branches are the gitty approach.\n\nAnother popular approach to resolving the divergence is what\n\"git pull --rebase\" would have done.\n\n(No one really likes what \"git pull\" would have done by default\n when there are local commits)\n\nTo implement the rebase workflow, do \"git rebase --autostash origin/master\".\nafter fetching.  That workflow is probably the simplest for\nfolks who eschew branching or are expats from other vcs.\n\nIf you're writing a tool you might want to check whether\nthe branch has an upstream configured via `git config branch.$name.remote`\nand `git config branch.$name.merge` as well.\n\n\n> I could try parsing a 'log' output; I have not investigated that.\n> This is easy in monotone; there is a command 'mtn heads' that gives this\n> result directly (it returns either one or two revs), and another command\n> 'mtn automate toposort' that orders revs topologically (by parent/child\n> relationships).\n\n`git log` can also tell you whether you have commits that they don't..\n\nWhat does origin/master have that I don't?\n\n\tgit log HEAD..origin/master\n\nWhat do I have that origin/master does not?\n\n\tgit log origin/master..HEAD\n\nThe git log output is easily controlled, though for these questions\nthe mere presence/absense of output tells you what you need to know.\n\ncheers,\n-- \nDavid\n"},{"id":"234069","messageId":"20140203005029.GF635004@vauxhall.crustytoothpaste.net","threadId":"35785","inReplyTo":"20140202230456.GA56790@gmail.com","subject":"Re: Determining update/merge/current state of a workspace","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2014-02-03T00:50:29Z","receivedAt":"2014-02-03T00:50:29Z","isPatch":false,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On Sun, Feb 02, 2014 at 03:04:59PM -0800, David Aguilar wrote:\n> I think you're looking for \"git merge-base\".\n> \n> If you do `git merge-base HEAD origin/master`\n> and its result is equal to `git rev-parse HEAD`\n> then you know that master is an ancestor of origin/master\n> and can be trivially fast-forwarded to origin/master.\n\nIn newer versions of git (1.8.0+), you can use \"git merge-base\n--is-ancestor\" for this instead.  The commit message for 5907cda implies\nthat it is more efficient than the old way.\n\n-- \nbrian m. carlson / brian with sandals: Houston, Texas, US\n+1 832 623 2791 | http://www.crustytoothpaste.net/~bmc | My opinion only\nOpenPGP: RSA v4 4096b: 88AC E9B2 9196 305B A994 7552 F1BA 225C 0223 B187\n"}]}