14

When maintaining a live system, I find that it is sometimes necessary to make ad-hoc temporary changes to files - changing logging levels, adding trace options to scripts etc.

When I do this, my semi-automated mechanisms for finding uncommitted changes and unmerged branches often show up false positives:

  • If I leave changes uncommitted, or just staged, then my checker script flags up the repo as dirty.
  • If I commit them as a "temporary changes commit", they get flagged up as 'changes ahead of the remote branch'
  • If I commit them on a new branch without a remote, they get flagged up as a 'branch without a remote'.

Normally, all of these are needed to find changes which haven't been merged up, but this also means that every way of 'hiding' temporary changes is blocked off too.

Note that I don't want to --assume-unchanged as the same file will often contain both temporary changes (which I don't want to be reminded about) and permanent changes (which I do), and looking through Handling temporary changes (not to be committed) in Git has no suggestions which address all of these requirements.

With Mercurial, I would look into using Mercurial Queues to get somewhere close to what I want. I would create a patch with my temporary changes, then if my analysis utility found a patch queue it would pop them, perform the analysis and then push them back. This would effectively remove only the temporary changes, perform the analysis on only the changes I haven't deemed temporary, and then re-apply those changes.

The trouble with any approach which changes the working directory is that this would affect the behaviour of the live system - for instance, our logging system checks for updates to the logging configuration every 10 seconds or so.

So, how can I best indicate to git that some changes are transient and shouldn't be committed and/or merged, while others should?

Mark Booth
  • 7,605
  • 2
  • 68
  • 92
  • 1
    Change the code in a way all the changes are made in unversioned config files? – choroba Mar 16 '16 at 12:29
  • I'm confused by this use case - you say you are making changes to a live system, but also indicate committing locally causes a problem with your checker of not having that branch as a remote. Can you provide some details on what the relationship is between this local repo, any live services, and the remote - i.e. what is the function of the local compared to the remote, etc.? – LightCC Oct 16 '17 at 22:12
  • I'm not sure what you are asking @LightCC. The local live repo contains the source code for the live system, the compiled code which is actually running the service (gitignored) and the configuration (spring, which may have changed temporarily). Is that the information you are after? The checker specifically looks for uncommitted changes, commites which haven't been pushed to the remote and branches which have no remote (so by implication haven't been pushed up) as these are all things I would normally want to know about, except when those changes are temporary - hence my question. – Mark Booth Oct 17 '17 at 11:04
  • So if I understand right, the remote is the central repository (is that Github, Github enterprise, etc.?), with the local being the live server, where you can pull down changes that have been made on the remote, possibly from other devs. Meanwhile, you also make changes on the live server itself (sounds dangerous), including local and temporary optimizations that you sometimes want to push back, sometimes not. Is that a fair characterization? – LightCC Oct 17 '17 at 21:09
  • Also, still unclear - you are definitely wanting to save the temp changes _in the repo_, as opposed to just a config file locally? – LightCC Oct 18 '17 at 02:09
  • Yes, some of the changes in the working directory might be temporary while others will need to be committed and pushed. Ideally the temp changes would only be in the working directory, I don't mind them being in the local repo, as long as they never make it to the remote. – Mark Booth Oct 18 '17 at 09:29

3 Answers3

2

For each of those files, you can setup a clean script which will be in charge of restoring the original content of those file as they were initially checked out.
That script can simply perform a git checkout -- afile, in order to restore its content to HEAD (discarding any local changes).

The restoration of the file through that script is automated through a content filter driver, using a .gitattributes declaration.

https://i.stack.imgur.com/tumAc.png
(image from "Customizing Git - Git Attributes" from "Pro Git book"))

Once you declare that content filer driver in your local git config, it will automatically, on git commit, restore the file.
Or it will consider the file as unchanged on git diff/git status.

See a complete example in "Best practice - Git + Build automation - Keeping configs separate".

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • At the moment I don't see how this would help, but I will investigate and perhaps update my question. – Mark Booth Oct 17 '17 at 11:27
  • @MarkBooth How would it *not* help? All your local modification would be ignored, as requested. – VonC Oct 17 '17 at 11:28
  • At a file level, though, as far as I can see. Not at the hunk level. I want some changes to a file to be marked as 'insignificant' so no warnings are triggered, while all other changes are warned about. If I were doing this with mercurial I would use Mercurial Queues to put insignificant changes into a patch, and pop it before analysis, pushing it back on afterwards. I'll edit my question. – Mark Booth Oct 17 '17 at 16:30
  • It can at the hunk level. But you need to script it – VonC Oct 17 '17 at 16:34
2

the same file will often contain both temporary changes (which I don't want to be reminded about) and permanent changes (which I do)

  • If I commit [the temporary changes] as a "temporary changes commit", they get flagged up as 'changes ahead of the remote branch'
  • If I commit them on a new branch without a remote, they get flagged up as a 'branch without a remote'.

If I were doing this with mercurial I would use Mercurial Queues to put insignificant changes into a patch, and pop it before analysis, pushing it back on afterwards.

Git's your servant. You decide what commits mean. Commit your changes and use Git to distribute the hunks so they have the most useful to you commit sequence[s] and boundaries. Keep insignificant changes in an "insignificant changes" commit, or have an "insignificant changes" branch, or whatever works best. Teach your checker to identify insignificant-changes commits.

From what you've given here a single ordinary "local" branch with a tip commit dedicated to config changes should do it, when you make a "significant" change, commit and use interactive or autosquash rebase to record the change behind the tip. Tell your checker to ignore anything in the local branch's tip; to update the local config, change it and git commit --amend --no-edit it, or if you goof and commit it separately then squash it with interactive rebase or equivalently just git reset --soft @~ and do the --amend --no-edit commit you meant to do. There's no need for special facilities, you get the full toolkit for whatever work you're doing. If there are commits regarded as particularly meaningful, put them someplace reserved for commits with that particular meaning.

jthill
  • 55,082
  • 5
  • 77
  • 137
  • Hmm, I wonder how easy it would be to write a `git-rot` which would *rotate* the *insignificant changes* commit up to the tip, pushing the *significant changes* commits behind them. That might make this workflow easier. I'll have a think about it. – Mark Booth Oct 23 '17 at 11:42
  • If commits are identifiable by their subject line you're in near-oneliner territory, a GIT_SEQUENCE_EDITOR sed script like (fingers-to-textbox warning) `GIT_SEQUENCE_EDITOR="sed -i '/insignificant/!{H;$!d};//!p;$g;p;d'" git rebase -i` should do it. – jthill Oct 23 '17 at 12:34
  • Oh, that's crafty. *8') There is good info in [How do I run git rebase --interactive in non-interactive manner?](https://stackoverflow.com/q/12394166/42473) – Mark Booth Oct 23 '17 at 14:41
  • And combining this with [git worktree](https://stackoverflow.com/a/35097583/42473) I should be able to do this without affecting the main working directory too. – Mark Booth Oct 23 '17 at 14:48
1

A few shots in the dark, trying to read between the lines with your ultimate goal/use case. This depends on exactly what you are able/willing to change in either your system or the semi-automated "repo health" system you have.

  1. Modify the system so that the configuration items that you need to change have both a "master/default" config file, and an untracked local/user config file. Any settings in the user config override the defaults.

    • Advantage: You can update your "repo health" scripts to WARN you about existing temporary settings (without requiring action), either all the time, or after a certain amount of time, etc.
    • Advantage: No need to do anything in Git itself.
    • Disadvantage: No tracking of what these settings were over time.
  2. Use special branch names/extensions, or use special tags to capture temporary setting commits, and update your "repo health" scripts to ignore those branches/tags (or have it WARN you, rather than force something?).

    • Example - name the branches that capture these with something special (always start with "tempsetting-", end with ".tmp", etc.), or commit them with a tmp branch, then tag them (again, probably with some special naming convention).
    • Advantage: Your temporary settings are captured in the repo, if that is desired
    • Disadvantage: More to do here - need to actively manage more than just a config file or user settings, etc.
LightCC
  • 9,804
  • 5
  • 52
  • 92
  • I had considered using a prefix or suffix on the commit message, and modifying the tools to take account of that, but if feels messy and I was hoping for a better solution. We already have an informal agreement that we can add "DO NOT MERGE" into commit messages and people won't action pull requests or submit gerrit changes with such commits. – Mark Booth Oct 17 '17 at 11:09
  • I'm thinking tags might be the better way to go. So you use a `tmp` or whatever branch to make a side commit, and then tag it with a special formatted tag. You can then delete the `tmp` branch and leave it hanging out with just the tag. I might need to do a little research on a few details, but that seems the best and most in-flow with Git, while still probably avoiding the downfalls of just having commits with different commit message special prefix/suffix, etc. - i.e. it's very easy to tell which commits are these special ones, etc. Should probably update the answer to be clearer. – LightCC Oct 17 '17 at 20:08